前言
本文从atomic 和 nonatomic 的区别说起,又叙述了一下@synchronized的作用。atomic 和 nonatomic 属性表示变量的原子性,nonatomic 表示不保证线程安全,但其实atomic也不能一定保证线程安全。
具体看一下编译器对atomic 和 nonatomic 做了什么。
nonatomic
如果一个属性为 nonatomic 时,默认的setter 和 getter 如下:
1 | @property(nonatomic, retain) UITextField *userName; |
atomic
如果一个属性为 nonatomic 时,默认的setter 和 getter 如下:
1 | @property(retain) UITextField *userName; |
可以看出相对于 nonatomic ,此时的 setter 和 getter 中多了一个 @synchronized(self)。
@synchronized 的作用是创建一个互斥锁,保证此时没有其它线程对 self 对象进行修改,其实这样子只保证了getter和setter存取方法的线程安全,并不能保证整个对象是线程安全的。
比如,线程A对一个变量write后,线程B再对同一个变量write,之后线程A对此变量read,这样子线程A会read到线程B的数据,显然是不安全的。
那么@synchronized 具体又是做什么的?
synchronized
编译器遇到@synchronized block
1 | @synchronized(self) { |
会转化为如下的代码,
1 | { |
可以看到,这里有两个函数 objc_sync_enter , objc_sync_exit,通过源码objc4-680来查看这两个函数:
1 | int objc_sync_enter(id obj) |
1 | int objc_sync_exit(id obj) |
从源码可以看出,有一个互斥锁data->mutex.lock(), data->mutex.tryUnlock() 来保证线程安全。
同时@synchronized(nil)是不起任何作用的。
继续看源码,发现mutex是一个递归锁,
1 | typedef struct SyncData { |
所以@synchronized(self)最终的效果如下:
1 | { |
递归锁也意味着如下代码没有问题:
1 | @synchronized (obj) { |
小结
- 你调用
sychronized的每个对象,Objective-C runtime都会为其分配一个递归锁并存储在哈希表中。 - 如果在
sychronized内部对象被释放或被设为 nil 看起来都 OK。不过这没在文档中说明,所以我不会再生产代码中依赖这条。 - 注意不要向你的
sychronized block传入 nil!这将会从代码中移走线程安全。你可以通过在objc_sync_nil上加断点来查看是否发生了这样的事情。