关于IOS 属性atomic(原子性)的理解

7,396 阅读3分钟

抛开语言限制说说什么是原子性:

原子性是指一个事物的操作是不可分割的,要么都发生,要么都不发生。

举个栗子🌰:(摘自某位不愿意透露姓名的大神

银行的转账业务就是一个原子性的操作。 张三到银行给李四转账1000元,张三卡里原来有2000元,李四卡里原来也有两千元,那么转账的步骤应该如下:

未命名文件-12.png
如果张三的钱扣完,银行系统瘫痪了,怎么办呢?张三的1000块钱会被会没呢,当然不会。这时候你的钱会退回来。也就是说银行的转账业务要么成功张三(1000元)李四(3000元),要么不发生张三(2000元)李四(2000元)。

那么回到我们OC中:(这里讲的是我们的Objective-c)

看看我们的atomic和nonatomic,我们通常的理解是线程安全和非线程安全,我觉得这只在语言层面上描述原子性造成的结果。

因为atomic描述的是属性赋值,属性赋值中还包含着很多其他操作,如访问对象,赋值等等,natomic是保证这个赋值的整个过程的完整性,并且不受其他线程的干扰,要么成功要么失败。

看个问题:为什么说atomic有时候无法保证线程安全呢?

先说我的结论: 用atomic修饰后,这个属性的setter、getter方法是线程安全的,但是对于整个对象来说不一定是线程安全的。

1.为什么setter、getter方法是线程安全的?

因为在setter和getter赋值取值的时候添加了自旋锁,不懂看这《oc中的线程锁》

// getter
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
   // ...
   if (!atomic) return *slot;

   // Atomic retain release world
   spinlock_t& slotlock = PropertyLocks[slot];
   slotlock.lock();
   id value = objc_retain(*slot);
   slotlock.unlock();
   // ...
}

// 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();
   }
   // ...
}

2.为什么说atomic没办法保证整个对象的线程安全,这里主要看一下网上主流的答案?

1.对于NSArray类型 @property(atomic)NSArray *array我们用atomic修饰,数组的添加和删除并不是线程安全的,这是因为数组比较特殊,我们要分成两部分考虑,一部分是&array也就是这个数组本身,另一部分是他所指向的内存部分。atomic限制的只是&array部分,对于它指向的对象没有任何限制。 atomic表示,我TM也很冤啊!!!!

2.当线程A进行写操作,这时其他线程的读或者写操作会因为该操作而等待。当A线程的写操作结束后,B线程进行写操作,然后当A线程需要读操作时,却获得了在B线程中的值,这就破坏了线程安全,如果有线程C在A线程读操作前release了该属性,那么还会导致程序崩溃。所以仅仅使用atomic并不会使得线程安全,我们还要为线程添加lock来确保线程的安全。 个人觉得这个就有点杠精的意味了,atomic还要管到你方法外面去了?????不过面试人家问你还要这么答,要严谨!!,

个人理解有问题大家多多提出,讨论中才能学习。

好文推荐: 《iOS atomic 对象是线程不安全的原因以及与 nonatomic 的区别》(这个名字很奇怪😄,说白了不安全是由OC对象的引用特性造成的,可以看下) 《事务四大特征:原子性,一致性,隔离性和持久性(ACID)》