Objective C 原子屬性

2021-08-21 19:27:32 字數 3672 閱讀 6775

objective-c 在宣告乙個屬性的時候,想必大家都是不用經過大腦思考就會寫@property (nonatomic, ...

我們都知道屬性可以是 nonatomic 也可以使 atomic 的,但是好像幾乎所有屬性在宣告的時候 nonatomic,atomic 的屬性幾乎沒出現過。atomic 修飾符彷彿已被大家遺忘。

實際上,如果宣告屬性時既不寫 atomic 也不寫 nonatomic,那麼這個屬性預設是 atomic 的

從字面上來看 nonatomic 是非原子的,atomic 是原子的。

atomic 的作用為:

atomic 修飾的屬性的寫操作是乙個原子操作。

什麼是原子操作?

原子操作就是指不會被執行緒排程機制打斷的操作。這個操作是乙個整體,cpu 一旦開始執行它,就會一直到執行結束,在這期間 cpu 不會轉而去執行其它執行緒的操作。

可以用**來模擬一下 atomic 的工作原理:

123

4567

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 能簡單的讓乙個屬性的寫操作變成執行緒安全的,為什麼幾乎不用它?

下面看乙個簡單的例子:

123

4567

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 的,在中間的某些列印日誌中,會發現有些數字被重複列印的兩次。

123

4567

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 的值後,再去寫,就會寫成一樣的值。

所以大部分情況下,為了保證執行緒安全,還是要自己加鎖,可以根據需要來保證某塊**整體的執行緒安全。

執行緒安全的**:

123

4567

89

- (void)dosomething 

nslog(@"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...