nonatomic 带来的线程安全问题
一、结论
一个对象对外暴露的读写属性,如果这个属性在多个线程中访问,可能会出现crash。因此对外暴露的属性一定要考虑线程安全问题。
二、看下面的代码
#import "ViewController.h" #import <malloc/malloc.h> #import <objc/runtime.h> @interface ViewController () @property (nonatomic,copy) NSString *name; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. dispatch_queue_t queue = dispatch_queue_create("abcde", DISPATCH_QUEUE_CONCURRENT); for (int i = 0 ; i < 10000; i ++) { dispatch_async(queue, ^{ NSLog(@"%@",[NSThread currentThread]); self.name = [NSString stringWithFormat:@"我%d",i]; }); } } @end
分析:
属性的set方法,ARC环境下,如果有两个线程同时给属性设置值时,就会同时把属性给release释放两次。过渡释放造成崩溃
属性是atomic时,不会崩溃,里面加了同步锁,set,get方法只能串行执行。
三、现象
产生的现象是一个线程访问这个对象的时候,这个对象已经释放,可能是错误的内存、野指针等情况。
其他解决方式:
其一:改为串行队列
dispatch_queue_t queue = dispatch_queue_create("abcde", DISPATCH_QUEUE_SERIAL); for (int i = 0 ; i < 10000; i ++) { dispatch_async(queue, ^{ NSLog(@"%@",[NSThread currentThread]); self.name = [NSString stringWithFormat:@"我%d",i]; }); }
其二:改为同步执行
dispatch_queue_t queue = dispatch_queue_create("abcde", DISPATCH_QUEUE_CONCURRENT); for (int i = 0 ; i < 10000; i ++) { dispatch_sync(queue, ^{ NSLog(@"%@",[NSThread currentThread]); self.name = [NSString stringWithFormat:@"我%d",i]; }); }
其三:加锁
NSLock *lock = [[NSLock alloc] init]; dispatch_queue_t queue = dispatch_queue_create("abcde", DISPATCH_QUEUE_CONCURRENT); for (int i = 0 ; i < 10000; i ++) { [lock lock]; dispatch_async(queue, ^{ NSLog(@"%@",[NSThread currentThread]); self.name = [NSString stringWithFormat:@"我%d",i]; [lock unlock]; }); }
其四:将字符串改为短的,变为标记指针 NSTaggedPointerString 类型,retain/release不起作用。如果字符串长度大于9或者如果有中文或其他特殊符号(可能是非 ASCII 字符)存在的话则会直接成为 __NSCFString 类型,__NSCFString与其他oc对象一样维护retainCount
dispatch_queue_t queue = dispatch_queue_create("abcde", DISPATCH_QUEUE_CONCURRENT); for (int i = 0 ; i < 10000; i ++) { dispatch_async(queue, ^{ NSLog(@"%@",[NSThread currentThread]); self.name = [NSString stringWithFormat:@"%d",i]; }); }
其五:将字符串改为 __NSCFConstantString 类型,retain/release不起作用(程序中内容相同的常量字符串只有一个)
dispatch_queue_t queue = dispatch_queue_create("abcde", DISPATCH_QUEUE_CONCURRENT); for (int i = 0 ; i < 10000; i ++) { dispatch_async(queue, ^{ self.name = @"哈哈哈哈哈哈哈哈哈哈哈哈"; }); }
在北京的灯中,有一盏是我家的。这个梦何时可以实现?哪怕微微亮。北京就像魔鬼训练营,有能力的留,没能力的走……