[英]what is the difference between a readwrite property and a nonatomic assign property?
[英]What's the difference between the atomic and nonatomic attributes?
屬性聲明中的atomic
和nonatomic
是什么意思?
@property(nonatomic, retain) UITextField *userName;
@property(atomic, retain) UITextField *userName;
@property(retain) UITextField *userName;
這三者在操作上有什么區別?
最后兩個是相同的; “atomic”是默認行為(
請注意,它實際上不是關鍵字;它僅通過缺少
——在最近的 llvm/clang 版本中添加了nonatomic
來指定
atomic
作為關鍵字)。
假設您正在 @synthesizing 方法實現,原子與非原子會更改生成的代碼。 如果您正在編寫自己的 setter/getter,則 atomic/nonatomic/retain/assign/copy 只是建議性的。 (注意:@synthesize 現在是 LLVM 最新版本中的默認行為。也不需要聲明實例變量;它們也會自動合成,並且會在名稱前加上_
以防止意外直接訪問)。
使用“原子”,合成的 setter/getter 將確保始終從 getter 返回整個值或由 setter 設置,而不管任何其他線程上的 setter 活動。 也就是說,如果線程 A 位於 getter 的中間,而線程 B 調用 setter,則實際可行的值——很可能是自動釋放的 object——將返回給 A 中的調用者。
在nonatomic
中,沒有做出這樣的保證。 因此, nonatomic
比“原子”快得多。
“原子”不做的是對線程安全做出任何保證。 如果線程 A 與線程 B 和 C 同時調用 getter 並使用不同的值調用 setter,則線程 A 可能會返回三個值中的任何一個 - 在調用任何 setter 之前的一個值或傳遞給 setter 的值之一在 B 和 C 中。 同樣,object 可能以 B 或 C 的值結束,無法判斷。
確保數據完整性——多線程編程的主要挑戰之一——是通過其他方式實現的。
添加到此:
當多個依賴屬性在起作用時,單個屬性的atomicity
性也不能保證線程安全。
考慮:
@property(atomic, copy) NSString *firstName;
@property(atomic, copy) NSString *lastName;
@property(readonly, atomic, copy) NSString *fullName;
在這種情況下,線程 A 可以通過調用setFirstName:
然后調用setLastName:
來重命名 object。 同時,線程 B 可能會在線程 A 的兩次調用之間調用fullName
,並將接收新的名字和舊的姓氏。
為了解決這個問題,您需要一個事務性 model 。 即某種其他類型的同步和/或排除允許一個人在更新依賴屬性時排除對fullName
的訪問。
這在 Apple 的文檔中進行了解釋,但以下是實際發生的一些示例。
請注意,沒有“atomic”關鍵字,如果不指定“nonatomic”,則該屬性是原子的,但顯式指定“atomic”會導致錯誤。
如果您不指定“非原子”,則該屬性是原子的,但如果您願意,您仍然可以在最近的版本中明確指定“原子”。
//@property(nonatomic, retain) UITextField *userName;
//Generates roughly
- (UITextField *) userName {
return userName;
}
- (void) setUserName:(UITextField *)userName_ {
[userName_ retain];
[userName release];
userName = userName_;
}
現在,原子變體有點復雜:
//@property(retain) UITextField *userName;
//Generates roughly
- (UITextField *) userName {
UITextField *retval = nil;
@synchronized(self) {
retval = [[userName retain] autorelease];
}
return retval;
}
- (void) setUserName:(UITextField *)userName_ {
@synchronized(self) {
[userName_ retain];
[userName release];
userName = userName_;
}
}
基本上,原子版本必須鎖定以保證線程安全,並且還會增加 object 上的引用計數(以及平衡它的自動釋放計數),以便保證調用者存在 object,否則存在如果另一個線程正在設置該值,則這是一個潛在的競爭條件,導致 ref 計數下降到 0。
實際上,這些事物的工作方式有很多不同的變體,具體取決於屬性是標量值還是對象,以及保留、復制、只讀、非原子等如何交互。 一般來說,屬性合成器只知道如何為所有組合做“正確的事情”。
了解差異的最佳方法是使用以下示例。
假設有一個名為“name”的原子字符串屬性,如果你從線程 A 調用 [self setName:@"A [self setName:@"A"]
],從線程 B 調用[self setName:@"B"]
,從線程 B 調用[self name]
線程 C,則不同線程上的所有操作將串行執行,這意味着如果一個線程正在執行 setter 或 getter,則其他線程將等待。
這使得屬性“name”讀/寫安全,但如果另一個線程 D 同時調用[name release]
,則此操作可能會導致崩潰,因為此處不涉及 setter/getter 調用。 這意味着 object 是讀/寫安全的(原子),但不是線程安全的,因為另一個線程可以同時向 object 發送任何類型的消息。 開發人員應確保此類對象的線程安全。
如果屬性“名稱”是非原子的,那么上面示例中的所有線程 - A、B、C 和 D 將同時執行,產生任何不可預知的結果。 在原子的情況下,A、B 或 C 中的任何一個將首先執行,但 D 仍然可以並行執行。
這個問題的其他優秀答案已經很好地定義了語法和語義。 由於執行和性能都不是很詳細,所以我將添加我的答案。
這3個之間的功能區別是什么?
我一直認為 atomic 作為默認值非常好奇。 在我們工作的抽象級別上,使用 class 的原子屬性作為實現 100% 線程安全的工具是一種極端情況。 對於真正正確的多線程程序,程序員的干預幾乎肯定是必需的。 同時,性能特征和執行還沒有深入詳細說明。 這些年來,我編寫了一些大量多線程的程序,我一直將我的屬性聲明為非nonatomic
的,因為原子對於任何目的都是不明智的。 在討論原子和非原子屬性的細節這個問題時,我做了一些分析,遇到了一些奇怪的結果。
執行
好的。 我想澄清的第一件事是鎖定實現是實現定義和抽象的。 Louis 在他的例子中使用了@synchronized(self)
——我認為這是一個常見的混淆來源。 該實現實際上並不使用@synchronized(self)
; 它使用 object 級自旋鎖。 Louis 的插圖非常適合使用我們都熟悉的構造進行高級插圖,但重要的是要知道它不使用@synchronized(self)
。
另一個區別是原子屬性將在 getter 中保留/釋放您的對象。
表現
這是有趣的部分:在無爭議(例如單線程)情況下使用原子屬性訪問的性能在某些情況下可能非常快。 在不太理想的情況下,使用原子訪問的開銷可能nonatomic
原子訪問的 20 倍以上。 而使用 7 個線程的Contested案例對於三字節結構(2.2 GHz Core i7 Quad Core,x86_64)慢了 44 倍。 三字節結構是一個非常慢的屬性的例子。
有趣的旁注:三字節結構的用戶定義訪問器比綜合原子訪問器快 52 倍; 或合成非原子訪問器速度的 84%。
有爭議的案件中的對象也可以超過 50 次。
由於實施中的優化和變化的數量,很難在這些環境中衡量現實世界的影響。 您可能經常聽到類似“相信它,除非您分析並發現它是一個問題”之類的話。 由於抽象級別,實際上很難衡量實際影響。 從配置文件中收集實際成本可能非常耗時,並且由於抽象,非常不准確。 同樣,ARC 與 MRC 可以產生很大的不同。
所以讓我們退后一步,不關注屬性訪問的實現,我們將包括像objc_msgSend
這樣的常見嫌疑人,並檢查在無爭議的情況下對NSString
getter 的許多調用的一些真實世界的高級結果(以秒為單位的值):
正如您可能已經猜到的那樣,引用計數活動/循環是原子和 ARC 下的重要貢獻者。 您還會在有爭議的案件中看到更大的差異。
雖然我很關注性能,但我還是說語義優先! . 同時,對於許多項目來說,性能是一個低優先級。 但是,了解您使用的技術的執行細節和成本當然不會受到傷害。 您應該根據自己的需要、目的和能力使用正確的技術。 希望這將為您節省幾個小時的比較時間,並幫助您在設計程序時做出更明智的決定。
原子= 線程安全
非原子= 沒有線程安全
如果實例變量在從多個線程訪問時行為正確,則無論運行時環境對這些線程執行的調度或交錯如何,並且調用代碼部分沒有額外的同步或其他協調,實例變量都是線程安全的。
如果一個線程更改了實例的值,則更改后的值可供所有線程使用,並且一次只有一個線程可以更改該值。
atomic
:如果要在多線程環境中訪問實例變量。
atomic
的含義: 不如nonatomic
快,因為nonatomic
在運行時不需要任何看門狗工作。
nonatomic
:如果實例變量不會被多個線程更改,則可以使用它。 它提高了性能。
在閱讀了這么多文章、Stack Overflow 帖子並制作了檢查變量屬性屬性的演示應用程序后,我決定將所有屬性信息放在一起:
atomic
// 默認nonatomic
strong = retain
// 默認weak = unsafe_unretained
retain
assign
// 默認unsafe_unretained
copy
readonly
readwrite
//默認在iOS 中的可變屬性屬性或修飾符文章中,您可以找到上述所有屬性,這肯定會對您有所幫助。
atomic
atomic
意味着只有一個線程訪問變量(靜態類型)。atomic
是線程安全的。atomic
是默認行為例子:
@property (retain) NSString *name; @synthesize name;
nonatomic
nonatomic
意味着多線程訪問變量(動態類型)。nonatomic
是線程不安全的。nonatomic
不是默認行為。 我們需要在屬性屬性中添加nonatomic
關鍵字。例子:
@property (nonatomic, retain) NSString *name; @synthesize name;
我在這里找到了對原子和非原子屬性的很好解釋。 以下是來自相同的一些相關文本:
“原子”意味着它不能被分解。 在操作系統/編程術語中,原子 function 調用是一個不能被中斷的調用 - 必須執行整個 function,並且在完成之前不能通過操作系統的通常上下文切換換出 CPU。 以防萬一您不知道:由於 CPU 一次只能做一件事,因此操作系統會在很小的時間片內輪換對所有正在運行的進程的 CPU 訪問,從而產生多任務處理的錯覺。 CPU 調度程序可以(並且確實)在進程執行的任何時候中斷進程 - 即使在 function 調用中間。 因此,對於像更新共享計數器變量這樣的操作,其中兩個進程可能會嘗試同時更新變量,它們必須“原子地”執行,即,每個更新操作必須全部完成,然后任何其他進程才能交換到中央處理器。
所以我猜想在這種情況下原子意味着屬性讀取器方法不能被中斷 - 實際上意味着方法讀取的變量不能在中途改變它們的值,因為其他一些線程/調用/函數得到換到CPU上。
因為atomic
變量不能被中斷,所以它們包含的值在任何時候都是(線程鎖)保證不會損壞,盡管確保這個線程鎖會使訪問它們變慢。 另一方面, non-atomic
變量不做這樣的保證,但確實提供了更快訪問的奢侈。 總而言之,當您知道多個線程不會同時訪問您的變量並加快速度時,go 具有non-atomic
。
原子保證對屬性的訪問將以原子方式執行。 例如,它總是返回一個完全初始化的對象,一個線程上的任何屬性的獲取/設置必須在另一個線程訪問它之前完成。
如果您想象以下 function 同時發生在兩個線程上,您就會明白為什么結果不會很漂亮。
-(void) setName:(NSString*)string
{
if (name)
{
[name release];
// what happens if the second thread jumps in now !?
// name may be deleted, but our 'name' variable is still set!
name = nil;
}
...
}
優點:每次返回完全初始化的對象使其成為多線程情況下的最佳選擇。
缺點:性能下降,執行速度稍慢
與 Atomic 不同,它不能確保完全初始化的 object 每次都返回。
優點:執行速度極快。
缺點:在多線程的情況下垃圾值的機會。
首先是最簡單的答案:您的后兩個示例之間沒有區別。 默認情況下,屬性訪問器是原子的。
非垃圾收集環境中的原子訪問器(即使用保留/釋放/自動釋放時)將使用鎖來確保另一個線程不會干擾正確設置/獲取值。
有關更多信息以及創建多線程應用程序時的其他注意事項,請參閱 Apple 的 Objective-C 2.0 文檔的“ 性能和線程”部分。
原子意味着只有一個線程訪問變量(靜態類型)。 Atomic 是線程安全的,但速度很慢。
非原子意味着多個線程訪問變量(動態類型)。 非原子是線程不安全的,但它很快。
Atomic 是線程安全的,它很慢,並且可以很好地保證(不保證)無論有多少線程嘗試訪問同一區域,都只提供鎖定的值。 使用 atomic 時,在此 function 中編寫的一段代碼成為臨界區的一部分,一次只能執行一個線程。
它只保證線程安全; 它不保證這一點。 我的意思是您為您的汽車聘請了一位專業的司機,但這並不能保證汽車不會發生事故。 然而,概率仍然微乎其微。
原子的 - 它不能被分解,所以結果是預期的。 使用非原子 - 當另一個線程訪問 memory 區域時,它可以修改它,因此結果出乎意料。
代碼對話:
Atomic 使屬性線程的 getter 和 setter 是安全的。 例如,如果你寫過:
self.myProperty = value;
是線程安全的。
[myArray addObject:@"Abc"]
不是線程安全的。
原子(默認)
Atomic 是默認值:如果您不鍵入任何內容,則您的屬性是原子的。 保證原子屬性,如果您嘗試從中讀取,您將返回一個有效值。 它不保證該值可能是什么,但您將獲得良好的數據,而不僅僅是垃圾 memory。 這允許您做的是,如果您有多個線程或多個進程指向一個變量,則一個線程可以讀取而另一個線程可以寫入。 如果它們同時命中,則保證讀取器線程獲得以下兩個值之一:更改之前或更改之后。 atomic 沒有為您提供任何關於您可能獲得哪些值的保證。 Atomic 通常與線程安全混淆,這是不正確的。 您需要以其他方式保證您的線程安全。 但是,atomic 將保證,如果您嘗試讀取,您將獲得某種價值。
非原子的
另一方面,正如你可能猜到的那樣,非原子只是意味着“不要做那些原子的事情”。 你失去的是保證你總能拿回一些東西。 如果您嘗試在寫入過程中讀取,則可能會取回垃圾數據。 但是,另一方面,你 go 快一點。 因為原子屬性必須做一些魔術來保證你會得到一個值,所以它們有點慢。 如果它是您經常訪問的屬性,您可能需要下拉到非原子以確保您不會受到速度損失。
在此處查看更多信息: https://realm.io/news/tmi-objective-c-property-attributes/
沒有這樣的關鍵字“原子”
@property(atomic, retain) UITextField *userName;
我們可以像上面一樣使用
@property(retain) UITextField *userName;
如果我使用 @property(atomic,retain)NSString *myString ,請參閱 Stack Overflow 問題。
默認值為atomic
,這意味着無論何時使用該屬性都會降低性能,但它是線程安全的。 Objective-C 所做的是設置一個鎖,因此只有實際線程可以訪問該變量,只要執行了 setter/getter。
帶有 ivar _internal 的屬性的 MRC 示例:
[_internal lock]; //lock
id result = [[value retain] autorelease];
[_internal unlock];
return result;
所以最后兩個是相同的:
@property(atomic, retain) UITextField *userName;
@property(retain) UITextField *userName; // defaults to atomic
另一方面, nonatomic
不會向您的代碼添加任何內容。 因此,如果您自己編寫安全機制,則只有線程安全。
@property(nonatomic, retain) UITextField *userName;
關鍵字根本不必寫為第一個屬性屬性。
不要忘記,這並不意味着屬性作為一個整體是線程安全的。 只有setter/getter的方法調用是。 但是,如果您使用一個 setter,然后同時使用一個帶有 2 個不同線程的 getter,它也可能被破壞!
如何申報:
由於 atomic 是默認的,所以,
@property (retain) NSString *name;
AND 在實現文件中
self.name = @"sourov";
假設與三個屬性相關的任務是
@property (retain) NSString *name;
@property (retain) NSString *A;
@property (retain) NSString *B;
self.name = @"sourov";
所有屬性並行工作(如異步)。
如果您從線程A調用“名稱”,
和
同時,如果你打電話
[self setName:@"Datta"]
從線程B ,
現在如果 *name 屬性是非原子的,那么
這就是為什么非原子被稱為線程不安全的原因但是由於並行執行,它的性能很快
現在如果 *name 屬性是原子的
這就是為什么原子被稱為線程安全,這就是為什么它被稱為讀寫安全
這種情況下的操作將連續執行。 並且性能緩慢
- 非原子意味着多線程訪問變量(動態類型)。
- 非原子是線程不安全的。
- 但它的性能很快
-Nonatomic 不是默認行為,我們需要在屬性屬性中添加 nonatomic 關鍵字。
對於在 Swift 中確認 Swift 屬性在 ObjC 意義上是非原子的。 一個原因是您考慮每個屬性的原子性是否足以滿足您的需求。
參考: https://forums.developer.apple.com/thread/25642
更多信息請訪問網站http://rdcworld-iphone.blogspot.in/2012/12/variable-property-attributes-or.html
如果您在多線程代碼中使用您的屬性,那么您將能夠看到非原子屬性和原子屬性之間的區別。 非原子比原子更快,原子是線程安全的,而不是非原子的。
Vijayendra Tripathi 已經給出了一個多線程環境的例子。
開始之前:您必須知道 memory 中的每個 object 都需要從 memory 中釋放,以便發生新的寫入程序。 你不能像在紙上那樣簡單地寫在上面。 您必須先擦除(dealloc)它,然后才能在其上寫入。 如果此時擦除完成(或一半完成)並且尚未寫入任何內容(或寫入一半)並且您嘗試閱讀它可能會非常有問題。 原子和非原子可幫助您以不同的方式處理此問題。
首先閱讀這個問題,然后閱讀Bbum 的答案。 另外,然后閱讀我的摘要。
atomic
將始終保證
保留計數是在 Objective-C 中管理 memory 的方式。 當您創建 object 時,它的保留計數為 1。當您向 object 發送保留消息時,其保留計數增加 1。當您發送 object 時,其保留計數減少 1 個釋放消息。向 object 發送一條自動釋放消息,它的保留計數在將來的某個階段減 1。 如果一個對象的保留計數減少到 0,它就會被釋放。
什么?! 多線程和線程安全有區別嗎?
是的。 多線程意味着:多個線程可以同時讀取一個共享的數據並且我們不會崩潰,但它不能保證您不是從非自動釋放的值中讀取。 使用線程安全,可以保證您閱讀的內容不會自動發布。 默認情況下我們不將所有內容都設為原子的原因是,存在性能成本,並且對於大多數事情來說並不真正需要線程安全。 我們代碼的一些部分需要它,對於這幾個部分,我們需要使用鎖、互斥鎖或同步以線程安全的方式編寫代碼。
nonatomic
總體而言,它們在兩個方面有所不同:
是否因為有或沒有自動釋放池而崩潰。
允許在“尚未完成寫入或空值”的中間讀取,或者不允許並且僅在值完全寫入時才允許讀取。
原子性 原子性(默認)
Atomic 是默認值:如果您不鍵入任何內容,則您的屬性是原子的。 保證原子屬性,如果您嘗試從中讀取,您將返回一個有效值。 它不保證該值可能是什么,但您將獲得良好的數據,而不僅僅是垃圾 memory。 這允許您做的是,如果您有多個線程或多個進程指向一個變量,則一個線程可以讀取而另一個線程可以寫入。 如果它們同時命中,則保證讀取器線程獲得以下兩個值之一:更改之前或更改之后。 atomic 沒有為您提供任何關於您可能獲得哪些值的保證。 Atomic 通常與線程安全混淆,這是不正確的。 您需要以其他方式保證您的線程安全。 但是,atomic 將保證,如果您嘗試讀取,您將獲得某種價值。
非原子的
另一方面,正如你可能猜到的那樣,非原子只是意味着“不要做那些原子的事情”。 你失去的是保證你總能拿回一些東西。 如果您嘗試在寫入過程中讀取,則可能會取回垃圾數據。 但是,另一方面,你 go 快一點。 因為原子屬性必須做一些魔術來保證你會得到一個值,所以它們有點慢。 如果它是您經常訪問的屬性,您可能需要下拉到非原子以確保您不會受到速度損失。 使用權
禮貌https://academy.realm.io/posts/tmi-objective-c-property-attributes/
原子性屬性屬性(原子性和非原子性)不會反映在相應的 Swift 屬性聲明中,但當從 ZAE832E9B5BDA2699DB45F3FA6AA8C 訪問導入的屬性時,Objective-C 實現的原子性保證仍然有效。
所以——如果你在 Objective-C 中定義了一個原子屬性,它在被 Swift 使用時將保持原子屬性。
禮貌https://medium.com/@YogevSitton/atomic-vs-non-atomic-properties-crash-course-d11c23f4366c
atomic 屬性確保保留完全初始化的值,而不管有多少線程在其上執行 getter 和 setter。
非原子屬性指定合成訪問器直接設置或返回一個值,不保證如果從不同線程同時訪問相同的值會發生什么。
原子意味着一次只有一個線程可以訪問變量(靜態類型)。 Atomic 是線程安全的,但速度很慢。
非原子意味着多個線程可以同時訪問變量(動態類型)。 非原子是線程不安全的,但它很快。
事實是他們使用自旋鎖來實現原子屬性。 代碼如下:
static inline void reallySetProperty(id self, SEL _cmd, id newValue,
ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
id oldValue;
id *slot = (id*) ((char*)self + offset);
if (copy) {
newValue = [newValue copyWithZone:NULL];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:NULL];
} else {
if (*slot == newValue) return;
newValue = objc_retain(newValue);
}
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];
_spin_lock(slotlock);
oldValue = *slot;
*slot = newValue;
_spin_unlock(slotlock);
}
objc_release(oldValue);
}
在一行中:
Atomic
是線程安全的。 Nonatomic
是線程不安全的。
如果您使用原子,則意味着線程將是安全且只讀的。 如果你使用的是非原子的,這意味着多個線程訪問變量並且是線程不安全的,但是它執行得很快,做了一個讀寫操作; 這是一種動態類型。
原子:通過使用 NSLOCK 鎖定線程來確保線程安全。
非原子:不確保線程安全,因為沒有線程鎖定機制。
為了簡化整個混淆,讓我們了解互斥鎖。
互斥鎖,顧名思義,鎖定了 object 的可變性。 因此,如果 object 被 class 訪問,則沒有其他 class 可以訪問相同的 ZA8CFDE6331BD59EB2AC96F8911C46。
在 iOS 中, @sychronise
還提供了互斥鎖。現在它以 FIFO 模式服務並確保流不受共享同一實例的兩個類的影響。 但是,如果任務在主線程上,請避免使用原子屬性訪問 object,因為它可能會占用您的 UI 並降低性能。
原子屬性:- 當分配有原子屬性的變量意味着它只有一個線程訪問並且它將是線程安全的並且從性能角度來看會很慢時,將具有默認行為。
非原子屬性:-當分配有非原子屬性的變量意味着它具有多線程訪問並且它不是線程安全的並且從性能角度來看會很快時,將具有默認行為並且當兩個不同的線程想要同時訪問變量時它會產生意想不到的結果。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.