阅读 15

记录NSArray修饰符问题

楔子

今天看到一个问题,我们常说@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;
复制代码

斎藤飛鳥は世界中に一番可愛い女の子ですから,彼女と付き合っきたいです。