浅谈Objective-C中的atomic和nonatomic

3,058 阅读3分钟

在谈atomic之前,有必要看下究竟什么是原子和原子操作。原子,顾名思义,就是最小的不可分割的粒子,在这里先不考虑夸克粒子,可以把原子这个词象征性化。原子操作,就是一个或一系列不可分割和阻断的操作。

原子(atomic)本意是“不能被进一步分割的最小粒子”,而原子操作(atomicoperation)意为“不可被中断的一个或一系列操作”。

atomic做了啥

在Objective-C中,property默认是atomic的。根据官方文档,设置原子性,通过setter和getter方法对数据进行读写时可以保证数据的准确性。因为它内部的实现是私有的,我们可以参考objc4的源码。

setter方法
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
    ...

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

    objc_release(oldValue);
}
getter方法
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
    ...

    // Retain release world
    id *slot = (id*) ((char*)self + offset);
    if (!atomic) return *slot;
        
    // Atomic retain release world
    spinlock_t& slotlock = PropertyLocks[slot];
    slotlock.lock();
    id value = objc_retain(*slot);
    slotlock.unlock();
    
    // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
    return objc_autoreleaseReturnValue(value);
}

通过源码可以看到,设置atomic之后,setter和getter方法在进行读写操作时会先进行加锁的动作,等操作完成之后再解锁。这样就保证的读写数据的完整性。

atomic能否保证线程安全

官方文档里有明确提示,原子性不等同于对象的线程安全。

Note: Property atomicity is not synonymous with an object’s thread safety.

文档中举了个简单的例子。在一个线程中,一个人的姓和名都在变。同一时间从另一个线程想获取这个人的姓名。 通过图可以看出,如果姓还未改时访问姓,名已改时访问名,这时候就可能会发生一个错配的情况。实际上这个人的名字只是从张三改成李四,而另一个线程可能获取到张四这个名。

这只是一个最简单的例子。其实还可以考虑比如property是一个可变数组等等。由此可见,atomic只是通过在setter和getter方法里加锁保证数据读写的完整性,并不能保证对象的线程安全。

为什么要用nonatomic

在iOS开发中,更多关注的是对象的线程安全,而property本身数据的原子性影响反而不是那么大。因为设置atomic,在set和get时,还有加锁和解锁的过程,增加性能的开销。设置nonatomic则可以免了这些动作。因此,如果需要在不同线程保证数据的准确性和一致性,则使用atomic,这里只对通过setter和getter方法对这个数据本身进行读写时有效。否则则可以设置为nonatomic。

You can use the nonatomic property attribute to specify that synthesized accessors simply set or return a value directly, with no guarantees about what happens if that same value is accessed simultaneously from different threads. For this reason, it’s faster to access a nonatomic property than an atomic one

参考