从MRC到ARC
10 Jun 2015前阵子Debug时遇上的坑, 示例代码见下.
@interface SomeObject : NSObject
@property NSMutableArray *array; // defaults to strong & atomic
@end
for (NSInteger i = 0; i < 200; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
_array = [NSMutableArray array];
});
}
乍看之下并没有问题. MRC下array最后会指向已经释放的NSMutableArray, 但不会crash, 不赘述; 在ARC下, 则几乎一定crash, 原因是release次数过多. 看起来
_array = [NSMutableArray array];
只是赋值, 但实际上ARC会将其转换为
NSMutableArray *temp = [NSMutableArray array];
[temp retain];
[_array release];
_array = temp;
而这一段非原子性操作暴露在并发下时, release过多就不难理解了.
相近的, 即使使用了getter, -addObject:时一样不能免于线程安全问题.
for (NSInteger i = 0; i < 200; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
self.array = [NSMutableArray array];
[self.array addObject:@"a"];
[self.array addObject:@"b"];
[self.array addObject:@"c"];
});
}
正确方法如下:
for (NSInteger i = 0; i < 200; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
NSMutableArray *array = [NSMutableArray array];
[array addObject:@"a"];
[array addObject:@"b"];
[array addObject:@"c"];
self.array = array;
});
}
结论1: 正确使用点语法可以有效规避并发错误, 但不包治百病.
结论2: 在并发线程中执行数据加工任务, 在同步线程中执行数据使用任务, 可以有效避免上述状况.
结论3: 珍爱生命, 使用Mutable类型时, 必须再三小心.