iOS-机制

iOS-atomic&nonatomic

2020-03-13  本文已影响0人  xxxxxxxx_123

前言

作为一个iOS开发人员,我们在日常的开发中经常会使用到nonatomic这个关键字,大家都知道nonatomic的意思是非原子性,与其相对的atomic原子性。但是非原子性和原子性到底是什么意思呢?他们到底做了什么、有什么不同呢?

原理

对属性设置相关的修饰词,在底层会调用如下方法:

void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
    reallySetProperty(self, _cmd, newValue, offset, true, false, false);
}

void objc_setProperty_nonatomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
    reallySetProperty(self, _cmd, newValue, offset, false, false, false);
}

void objc_setProperty_atomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
    reallySetProperty(self, _cmd, newValue, offset, true, true, false);
}

void objc_setProperty_nonatomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
    reallySetProperty(self, _cmd, newValue, offset, false, true, false);
}

static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
    if (offset == 0) {
        object_setClass(self, newValue);
        return;
    }

    id oldValue;
    id *slot = (id*) ((char*)self + offset);

    if (copy) {
        newValue = [newValue copyWithZone:nil];
    } else if (mutableCopy) {
        newValue = [newValue mutableCopyWithZone:nil];
    } else {
        if (*slot == newValue) return;
        newValue = objc_retain(newValue);
    }

    if (!atomic) {
        oldValue = *slot;
        *slot = newValue;
    } else {
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;        
        slotlock.unlock();
    }

    objc_release(oldValue);
}

上述代码中,*slot是所修饰对象的地址。我们可以看出,nonatomic在底层的赋值是操作是直接将新值赋给了所修饰的对象:

oldValue = *slot;
*slot = newValue;

atomic在底层的赋值操作是:

spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;        
slotlock.unlock();

首先,生成了一个spinlock_t的锁,加锁,赋值,解锁。现在的已经不是原来的自旋锁,而是os_unfair_lock

using spinlock_t = mutex_tt<LOCKDEBUG>;
class mutex_tt : nocopy_t {
    os_unfair_lock mLock;
    
    void lock() {
        lockdebug_mutex_lock(this);
        os_unfair_lock_lock_with_options_inline
            (&mLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
    }

    void unlock() {
        lockdebug_mutex_unlock(this);
        os_unfair_lock_unlock_inline(&mLock);
    }
}

getter的底层实现:

id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
    if (offset == 0) {
        return object_getClass(self);
    }

    id *slot = (id*) ((char*)self + offset);
    if (!atomic) return *slot;
        
    spinlock_t& slotlock = PropertyLocks[slot];
    slotlock.lock();
    id value = objc_retain(*slot);
    slotlock.unlock();
    return objc_autoreleaseReturnValue(value);
}

我们可以看出,nonatomic是直接返回,而atomic也会在取值的过程中也是加了一把锁。

所以,我们可以得出结论:nonatomicatomic的区别是setter/getter方法会不会加锁atomic会对setter/getter方法进行加锁操作,而nonatomic不会。

也就是说,被atomic修饰的属性,当前有两个线程,线程1和线程2,线程1执行getter的时候,线程2如果想要执行setter,必须要线程1执行完getter才行。

既然atomic的对属性的setter/getter加了锁,那么它修饰的属性是绝对安全的么?下面,我们看一个例子:

@property (atomic, strong) NSArray *atomicArray;

- (void)atomicTest {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (int i = 0; i < 100000; i ++) {
            if (i % 2 == 0) {
                self.atomicArray = @[@"AAA", @"BBB", @"CCC"];
            } else {
                self.atomicArray = @[@"DDD"];
            }
            NSLog(@"Thread A: %@\n", self.atomicArray);
        }
    });
    
    //Thread B
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (int i = 0; i < 100000; i ++) {
            if (self.atomicArray.count >= 2) {
                NSString* str = [self.atomicArray objectAtIndex:1];
                NSLog(@"==str==%@==", str);
            }
            NSLog(@"Thread B: %@\n",self.atomicArray);
        }
    });
}

运行程序,我们发现程序会崩溃,直接抛出如下异常:

libsystem_kernel.dylib`__pthread_kill:

这是为什么呢?atomic只是保证了setter/getter的安全性,但是并不能其属性的线程安全。因为我们对属性进行了[self.atomicArray objectAtIndex:1]取值操作,虽然self.atomicArraygetter是安全的,但是其objectAtIndex却不是安全的。

结论

atomic会对setter/getter方法进行加锁操作,而nonatomic不会。所以在性能上nonatomic要优于atomic。而atomic只是对属性的setter/getter方法进行了加锁操作,这种安全仅仅是set/get的读写安全,并非真正意义上的线程安全,因为属性的线程安全还有读写之外的其他操作。因此,在日常的开发中,使用atomic还是需要慎重。

上一篇 下一篇

猜你喜欢

热点阅读