测试代码
@interface ViewController ()
@property (nonatomic, strong)NSObject *obj;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
for (int i = 0; i < 10000000; i++) {
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(func) object:nil];
[thread start];
}
}
-(void)func
{
self.obj = [[NSObject alloc] init];
}
@end
atomic和nonatomic原理
来看一下苹果开源的objc4源码,在 objc-accessors.mm文件中有很多的 getter 与 setter 方法的实现,这些实现都是为了 @property 做准备的,最终setter方法都会调到reallySetProperty()这个方法中,代码如下:
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic)
{
id oldValue;
id *slot = (id*) ((char*)self + offset);
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
atomic 与 nonatomic 的区别仅仅是对oldValue和slot赋值是否加锁。多线程中操作nonatomic属性时,如果某一线程在oldValue赋值后中断,那么另一线程执行oldValue赋值时,由于没有执行 *slot = newValue赋值和objc_release释放原值,所以oldValue赋值还是之前的,那么此时2个线程中oldValue指向的都是线程中断前的值,执行objc_release时释放2次,引起crash
解决方法
解决方法非常简单,nonatomic改成atomic即可
展开来说,这个问题是多线程写临界区的问题,引起crash并不奇怪