多线程操作nonatomic属性引起的crash

测试代码

@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
屏幕快照 2017-10-18 下午1.52.38.png

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并不奇怪

你可能感兴趣的:(多线程操作nonatomic属性引起的crash)