iOS 深入瞭解 atomic 與 nonatomic
前言:已經多年移動端開發的我宣佈Property Attributes一直以來都是用nonatomic,尚未使用到atomic。
場景:如今專案中有這樣一個場景,在一個自定義型別的Property在一個執行緒中改變的同時也要同時在另一個執行緒中使用它,使我不得不將Property定義成atomic,但是由此發現atomic並不會保證執行緒安全,由此我深入查詢瞭解下atomic 與 nonatomic,發現自己之前並不瞭解atomic 與 nonatomic。
正文:
首先,我們先要弄懂一個問題:什麼是原子性?
原子操作是不可分割的操作,在原子操作執行完畢之前,其不會被任何其它任務或事件中斷。
被標註atomic會保證這種對Property的頻繁操作的原子性,可以避免由兩個操作對同一個Property同時進行操作而造成的錯誤。
atomic與nonatomic內部實現的區別只是atomic物件setter和getter方法會加一個鎖,而nonatomic並沒有,程式碼如下:
@property (nonatomic) NSObject *nonatomicObj; @property (atomic) NSObject *atomicObj; - (void)setNonatomicObj:(NSObject *)nonatomicObj{ if (_nonatomicObj != nonatomicObj) { [_nonatomicObj release]; _nonatomicObj = [nonatomicObj retain]; } } - (NSObject *)nonatomicObj{ return _nonatomicObj; } - (void)setAtomicObj:(NSObject *)atomicObj{ @synchronized(self) { if (_atomicObj != atomicObj) { [_atomicObj release]; _atomicObj = [atomicObj retain]; } } } - (NSObject *)atomicObj{ @synchronized(self) { return _atomicObj; } }
其次,原子性是不是代表執行緒安全?
我們先看一下蘋果開發文件
蘋果開發文件已經明確指出:Atomic不能保證物件多執行緒的安全。所以Atomic 不能保證物件多執行緒的安全。它只是能保證你訪問的時候給你返回一個完好無損的Value而已。舉個例子:
如果執行緒 A 調了 getter,與此同時執行緒 B 、執行緒 C 都調了 setter——那最後執行緒 A get 到的值,有3種可能:可能是 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同時,最終這個屬性的值,可能是 B set 的值,也有可能是 C set 的值。所以atomic可並不能保證物件的執行緒安全。
atomic和nonatomic的對比:
1、atomic和nonatomic用來決定編譯器生成的getter和setter是否為原子操作。
2、atomic:系統生成的 getter/setter 會保證 get、set 操作的完整性,不受其他執行緒影響。getter 還是能得到一個完好無損的物件(可以保證資料的完整性),但這個物件在多執行緒的情況下是不能確定的,比如上面的例子。
也就是說:如果有多個執行緒同時呼叫setter的話,不會出現某一個執行緒執行完setter全部語句之前,另一個執行緒開始執行setter情況,相當於函式頭尾加了鎖一樣,每次只能有一個執行緒呼叫物件的setter方法,所以可以保證資料的完整性。
atomic所說的執行緒安全只是保證了getter和setter存取方法的執行緒安全,並不能保證整個物件是執行緒安全的。
3、nonatomic:就沒有這個保證了,nonatomic返回你的物件可能就不是完整的value。因此,在多執行緒的環境下原子操作是非常必要的,否則有可能會引起錯誤的結果。但僅僅使用atomic並不會使得物件執行緒安全,我們還要為物件執行緒新增lock來確保執行緒的安全。
4、nonatomic的速度要比atomic的快。
5、atomic與nonatomic的本質區別其實也就是在setter方法上的操作不同
總結:
所以atomic的作用只是保證了Property的原子性,在多執行緒環境下同時操作它時,無論何時取值,都可以取到一個完整值,但是並不能保證執行緒安全,具體例子參照上文。所以如果想要保證執行緒安全,單單把用atomic來標註是完全不夠的,還需要通過上鎖或其他方式老保證執行緒安全!