在本文中安皱,你將了解到如下內(nèi)容:
- 什么是原子性
atomic
和nonatomic
的區(qū)別- 實(shí)現(xiàn)
atomic
的鎖 -os_unfair_lock
什么是原子性
在開始了解atomic
和nonatomic
的區(qū)別之前呢俭识,我們需要先理解一個(gè)詞:原子性
案训。
指事務(wù)的不可分割性肖抱,一個(gè)事務(wù)的所有操作要么不間斷地全部被執(zhí)行软棺,要么一個(gè)也沒有執(zhí)行猴誊。
這是《計(jì)算機(jī)科學(xué)技術(shù)名詞 》第三版中對(duì)原子性
的定義阁将。
為了直觀的了解事務(wù)的不可分割性
膏秫,我們假定有一個(gè)屬性name
如下定義:
@property (atomic, copy) NSString *name;
如果我們來自定義setter
,我們會(huì)使用_name = name.copy;
對(duì)_name
的賦值做盅。
這只是我們使用的一個(gè)簡(jiǎn)單的賦值操作缤削,如果對(duì)其進(jìn)行拆分,可以分為兩個(gè)步驟:
- 對(duì)
name
進(jìn)行copy
操作吹榴。 - 將
copy
后的指針賦值給_name
亭敢。
這樣的代碼,在單線程的環(huán)境下執(zhí)行腊尚,不會(huì)有任何問題吨拗。但是在多線程環(huán)境下,如果有多個(gè)線程在相近的時(shí)間對(duì)屬性name
進(jìn)行賦值婿斥,name
最后的值,可能是任意一個(gè)線程中賦予的值哨鸭。甚至我們多次執(zhí)行的結(jié)果不一致民宿。
這是因?yàn)槎鄠€(gè)線程幾乎同時(shí)開始執(zhí)行步驟1,而步驟1執(zhí)行完成的順序是隨機(jī)的像鸡,執(zhí)行完步驟1之后活鹰,再執(zhí)行步驟2對(duì)_name
進(jìn)行賦值,那么name
的值只估,就是最后開始執(zhí)行步驟2的線程所賦予的值志群。
如果是原子性
的,那么就會(huì)保證如果線程A開始執(zhí)行步驟1蛔钙,那么其它線程就無法開始執(zhí)行步驟1了锌云,必須要等到線程A執(zhí)行完步驟2之后,其它線程才可以開始執(zhí)行步驟1吁脱。
atomic
和nonatomic
的區(qū)別
由上面的解釋我們明確的知道桑涎,atomic
和nonatomic
的區(qū)別就是atomic
的getter
和setter
操作都是原子性
彬向,而nonatomic
并不是。
既然nonatomic
并不是原子性的攻冷,可能會(huì)在多線程環(huán)境下出現(xiàn)數(shù)據(jù)錯(cuò)亂的問題娃胆,那為什么我們?cè)诖a中,通常都是使用nonatomic
等曼,卻很少使用atomic
呢里烦?
我們使用nonatomic
是基于兩點(diǎn)考慮:
- 我們的代碼大多不會(huì)出現(xiàn)對(duì)屬性進(jìn)行并發(fā)的
get
和set
,所以在很大的程度上是安全的禁谦。 - 性能問題胁黑,要保證
atomic
的原子性
需要付出一定的性能下降的代價(jià)。
實(shí)現(xiàn)atomic
的鎖 - os_unfair_lock
蘋果底層對(duì)atomic
的實(shí)現(xiàn)枷畏,實(shí)際上是使用了os_unfair_lock
這個(gè)鎖别厘。
對(duì)于屬性自動(dòng)生成的getter
和setter
方法,大致如下:
// 初始化鎖
os_unfair_lock_t unfairLock;
unfairLock = &(OS_UNFAIR_LOCK_INIT);
- (void)setName:(NSString *)name {
// 加鎖
os_unfair_lock_lock(unfairLock);
_name = name.copy;
// 解鎖
os_unfair_lock_unlock(unfairLock);
}
- (NSString *)name {
NSString *name = nil;
// 加鎖
os_unfair_lock_lock(unfairLock);
name = _name;
// 解鎖
os_unfair_lock_unlock(unfairLock);
return name;
}
由于加鎖拥诡,所以對(duì)比性能的話触趴,atomic
會(huì)低于nonatomic
。