楔子
今天看到一个问题,我们常说@property修饰符中,copy修饰NSString和block对象,其余对象强引用的情况下使用Strong即可,但NSArray使用Strong会引起某些异常导致数组数据错乱。
测试用例
这里使用两个不同修饰符修饰的Array
@property (nonatomic,copy) NSArray *copArray;
@property (nonatomic,strong) NSArray *strongArray;
赋值:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.copArray = [NSArray array];
self.strongArray = [NSArray array];
self.otherStrongArray = [NSArray array];
NSMutableArray *mArray = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
NSLog(@"before self.copArray:%p self.strongArray:%p other:%p mArray:%p",self.copArray,self.strongArray,self.otherStrongArray,mArray);
NSLog(@"===================");
self.copArray = mArray;
self.strongArray = mArray;
self.otherStrongArray = [NSArray arrayWithArray:mArray];
NSLog(@"before self.copArray:%@ self.strongArray:%@ mArray:%@",self.copArray,self.strongArray,mArray);
NSLog(@"------------------");
[mArray addObject:@"3"];
NSLog(@"afterself.copArray:%@ self.strongArray:%@ otherArray:%@ mArray:%@",self.copArray,self.strongArray,self.otherStrongArray,mArray);
NSLog(@"===================");
NSLog(@"after self.copArray:%p self.strongArray:%p otherStrong:%p mArray:%p",self.copArray,self.strongArray,self.otherStrongArray,mArray);
}
打印结果:
self.copArray:( 1, 2 ) self.strongArray:( 1, 2, 3 ) mArray:( 1, 2, 3 )
这里看到 self.strongArray的数据改变了,并不是我们想要的结果。所以我又打印了一下三个数组各自的内存地址
self.copArray:0x600003101f60 self.strongArray:0x600003f7e1c0
otherStrong:0x600003101fa0
mArray:0x600003f7e1c0
发现self.strongArray的内存地址已经指向了mArray。self.copArray仍是自己的0x600003101f60。查看赋值前地址是
self.copArray:0x7fff8062d570
self.strongArray:0x7fff8062d570
mArray:0x600003f7e1c0
在这里又创建了strong修饰符的otherStrongArray赋值
self.otherStrongArray = [NSArray arrayWithArray:mArray];
结果是
otherArray:( 1, 2 )
没有出现self.strongArray数据异常的情况
猜测出现问题的原因:
self.copArray = mArray;
self.strongArray = mArray;
self.otherStrongArray = [NSArray arrayWithArray:mArray];
这里self.strongArray = mArray; 本质上是调用了ViewController的- setStrongArray:方法,其内部大概会转换成这样:
- (void)setStrongArray:(NSArray *)strongArray{
if (_strongArray != strongArray) {
[_strongArray release];
[strongArray retain];
_strongArray = strongArray;
}
}
只是对传入的参数mArray做了一次retain操作,使mArray引用计数器+1,实际指向的还是mArray,所以会导致出现数据错乱。 而self.copArray = mArray内部大概会转换成这样:
- (void)setCopArray:(NSArray *)copArray{
if (_copArray != copArray) {
[_copArray release];
NSArray *abc = [_copArray copy];
_copArray = abc;
}
}
这里self.copArray是深拷贝了mArray的元素,创建了一块新的abc空间赋值给_copArray,以至于修改mArray并不会对self.copArray出现影响。
结论
self.strongArray仅仅是指向了mArray的地址,并没有创建自己的内存空间,为了避免这种情况,尽量使用copy来修饰NSArray,否则就要时刻注意不要修改指针的指向。
PS: UIView的subViews数组也是copy,我们编码风格最好向apple风格靠近
@property(nonatomic,readonly,copy) NSArray<__kindof UIView *> *subviews;
斎藤飛鳥は世界中に一番可愛い女の子ですから,彼女と付き合っきたいです。