objective-c 在宣告乙個屬性的時候,想必大家都是不用經過大腦思考就會寫@property (nonatomic, ...
。
我們都知道屬性可以是 nonatomic 也可以使 atomic 的,但是好像幾乎所有屬性在宣告的時候 nonatomic,atomic 的屬性幾乎沒出現過。atomic 修飾符彷彿已被大家遺忘。
實際上,如果宣告屬性時既不寫 atomic 也不寫 nonatomic,那麼這個屬性預設是 atomic 的。
從字面上來看 nonatomic 是非原子的,atomic 是原子的。
atomic 的作用為:
atomic 修飾的屬性的寫操作是乙個原子操作。什麼是原子操作?
原子操作就是指不會被執行緒排程機制打斷的操作。這個操作是乙個整體,cpu 一旦開始執行它,就會一直到執行結束,在這期間 cpu 不會轉而去執行其它執行緒的操作。
可以用**來模擬一下 atomic 的工作原理:
1234567
891011
1213
1415
1617
1819
20
#import "viewcontroller.h"@inte***ce viewcontroller ()
@property (atomic, assign) nsinteger count;
@end
@implementation viewcontroller
@synthesize count = _count;
- (void)setcount:(nsinteger)count
}- (nsinteger)count
...
看上面**,viewcontroller 有個 count 屬性,我們重寫了它的 setter 和 getter 方法,在 setter 方法中,通過@synchronized (self) {}
為複製操作_count = count
加了一把鎖,使得賦值這個操作同一時間只能有乙個執行緒執行,保證了寫屬性值的時候的執行緒安全。
上面**實現了和 atomic 相同的功能,但是底層的工作方式還是有區別的。我們常常用@synchronized
來加鎖,這種鎖是互斥鎖。而 atomic 修飾的屬性自帶了一把自旋鎖。
互斥鎖和自旋鎖的區別:鎖名
作用互斥鎖
當某個資源被先進入的執行緒上了鎖以後,其它後面進入的執行緒會進入休眠狀態。當鎖釋放後,進入休眠狀態的執行緒變為喚醒狀態。
自旋鎖當某個資源被先進入的執行緒上了鎖以後,其它後進入的執行緒會開啟乙個迴圈,不斷檢查鎖有沒有釋放,當鎖釋放後,退出迴圈開始訪問資源,整個過程中後進入的執行緒一直保持執行狀態。
既然 atomic 能簡單的讓乙個屬性的寫操作變成執行緒安全的,為什麼幾乎不用它?
下面看乙個簡單的例子:
1234567
891011
1213
1415
1617
1819
2021
2223
2425
2627
2829
#import "viewcontroller.h"@inte***ce viewcontroller ()
@property (atomic, assign) nsinteger count;
@end
@implementation viewcontroller
- (void)viewdidload
- (void)dosomething
}@end
上面**中,把屬性 count 宣告為 atomic 的。在 viewdidload 中建立了兩個執行緒 threada 和 threadb,都去執行 dosomething 方法。在 dosomething 方法中,去給 self.count 的值通過每次迴圈 +1 增加 10 次,然後列印 self.count 的值。為了讓異常情況出現的概率提高,加入一句[nsthread sleepfortimeinterval:1.0];
。
執行上面的**,會發現列印的結果中,最後一條 self.count 的值往往是小於 20 的,在中間的某些列印日誌中,會發現有些數字被重複列印的兩次。
1234567
8
...2018-02-07 23:05:08.718744+0800 atomicdemo[53388:2777211] self.count = 13
2018-02-07 23:05:08.718791+0800 atomicdemo[53388:2777210] self.count = 14
2018-02-07 23:05:09.719374+0800 atomicdemo[53388:2777210] self.count = 15
2018-02-07 23:05:09.719374+0800 atomicdemo[53388:2777211] self.count = 15
2018-02-07 23:05:10.719673+0800 atomicdemo[53388:2777211] self.count = 17
2018-02-07 23:05:10.719673+0800 atomicdemo[53388:2777210] self.count = 16
...
上面的結果中 15 出現了兩次,這說明在使用 atomic 的情況下,還是出現了資源競爭。
那麼原因在**呢?
我們看這句**:
1
self.count++;
這句**做了兩件事,先讀取 self.count 的值,然後把讀取到的值 + 1 後賦值給 self.count。
由於 atomic 僅僅能保證寫是執行緒安全的,而不是保證 讀 -> +1 -> 寫,這個整體是執行緒安全的。
當兩個執行緒都執行到讀取完 self.count 的值後,再去寫,就會寫成一樣的值。
所以大部分情況下,為了保證執行緒安全,還是要自己加鎖,可以根據需要來保證某塊**整體的執行緒安全。
執行緒安全的**:
1234567
89
- (void)dosomethingnslog(@"self.count = %@ %@", @(self.count), [nsthread currentthread]);
}}
因為 atomic 在大部分情況下都無法保證執行緒安全,並且 atomic 的屬性因為增加了原子性而降低了執行效率,因此實際開發中幾乎不會出現 atomic 的身影。
最後簡單對比一下 nonatomic 和 atomic
修飾符優勢
劣勢nonatomic
執行效率高,效能好
不是執行緒安全的
atomic
執行緒安全,但是僅能保證寫操作的執行緒安全
大幅降低執行效率
1 原子屬性和非原子屬性
1.原子屬性和非原子屬性 oc在定義屬性時有 atomic 和 nonatomic 兩種選擇 atomic 預設屬性 原子屬性,自動為setter 方法加鎖 執行緒安全的,需要消耗大量的 cpu 資源 nonatomic 非原子屬性,不會為 setter 方法加鎖 非執行緒安全的,適合記憶體小的移動...
原子和非原子屬性
一 原子和非原子屬性 1.oc在定義屬性時有nonatomic和atomic兩種選擇 atomic 原子屬性,為setter方法加鎖 預設就是atomic nonatomic 非原子屬性,不會為setter方法加鎖。2.nonatomic和atomic的對比 atomic 執行緒安全,需要消耗大量的...
Objective C 屬性詳解
屬性作用 自動生成setter和getter方法 屬性定義 property 屬性的型別 型別與內部操作的例項變數的型別相同 屬性名 和內部操作例項變數名相同 屬性在.h檔案中,自動生成的是setter和getter方法的宣告 屬性特性,1.讀寫特性 1 可讀可寫 讀,getter方法 寫,sett...