美文网首页iOS-机制
iOS-atomic&nonatomic

iOS-atomic&nonatomic

作者: xxxxxxxx_123 | 来源:发表于2020-03-13 16:52 被阅读0次

    前言

    作为一个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还是需要慎重。

    相关文章

      网友评论

        本文标题:iOS-atomic&nonatomic

        本文链接:https://www.haomeiwen.com/subject/fmudshtx.html