正常来讲,将一个 NSMutableArray 对象转换成 NSArray对象 可以用copy方法,比如:
NSMutableArray* testMutableArray = [NSMutableArray new];
NSArray *testArray = [testMutableArray copy];
这种情况下,testArray 其实指向的是 testMutableArray 的一份深度拷贝(参考这里)。但是仔细看一下:
[testMutableArray copy]
按说返回的应该是个可变对象,但是现在用一个不可变类的指针指向了它,这里面其实有隐含的类型转换。下面这个例子就更明显一些:
NSMutableArray* mary = [NSMutableArray new];
NSArray* ary = mary;
NSLog(@"%d %d", mary.count, ary.count);
[mary addObject:@""];
NSLog(@"%d %d", mary.count, ary.count);
我们生成一个可变数组,并把它赋值给一个不可变数组的指针。从执行结果来看,第一个NSLog输出两个0, 第二个NSLog则会输出两个1。也就是说,在这种情况下看起来,一个不可变对象其实也变成了“可变的”。
这其实会引入一些潜在风险,比如:
- (NSArray*)getArray {
NSMutableArray* mary = [NSMutableArray new];
...
return mary;
}
这段Xcode并不会给出任何警告,但如果你认为返回结果是“不可变”对象,那么就有危险了。
今天在使用一个相册的返回结果的时候,就遇到了问题。相册使用一个NSArray返回被选择的Asset集合,我本来认为是安全的,但在使用过程中发现返回结果会在某种情况下被莫名清除,仔细排查才发现,相册内部为了提高效率,直接把一个可变对象使用隐含转换返回了一个不可变结果,但同时在程序的其他地方,还能够继续对这个对象进行操作。
OC在本质上继承了C语言的灵活特性,隐式类型转换就是其中之一,这在现代语言中是被逐渐限用的特性,也因此容易引发各种问题。