[OC]之 atomic 與 nonatomic的區別

weixin_33797791發表於2017-08-04

我們宣告屬性的時候,必須要考慮到屬性特質對編譯器所產生的存取方法的影響。

下面我們就主要講講atomic 與 nonatomic:

在預設情況下,由編譯器所合成的方法會通過鎖定機制確保其原子性(atomicity)。如果屬性具備nonatomic特質,則不需要同步鎖。

請注意!!!!儘管沒有名為atomic的特質(如果某屬性不具備nonatomic特質,那它就是“原子的”(atomic)),但是仍然可以在屬性特質中寫明這一點,編譯器是不會報錯的。

那麼atomic與nonatomic的區別又是什麼捏?

之前說過滴,具備atomic特質的獲取方法會通過鎖定機制來確保其操作的原子性。

也就是說,如果兩個執行緒同時讀取一個屬性,那麼不論何時,總能看到有效的屬性值。

如果不加鎖的話(或者說使用nonatomic語義),那麼當其中一個執行緒正在改寫某屬性值的時候,另外一個執行緒也許會突然闖入,把尚未修改好的屬性值讀取出來。發證這種情況時,執行緒讀取道德屬性值肯能不對。

相信大家都遇到過上述那種情況吧。。。。

一般iOS程式中,所有屬性都宣告為nonatomic。這樣做的原因是:

在iOS中使用同步鎖的開銷比較大, 這會帶來效能問題。一般情況下並不要求屬性必須是“原子的”,因為這並不能保證“執行緒安全”(thread safety),若要實現“執行緒安全”的操作,還需採用更為深層的鎖定機制才醒。

例如:一個執行緒在連續多次讀取某個屬性值的過程中有別的執行緒在同時改寫該值,那麼即便將屬性宣告為atomic,也還是會讀取到不同的屬性值。

因此,iOS程式一般都會使用nonatomic屬性。但是在Mac OS X程式時, 使用atomic屬性通常都不會有效能瓶頸

因為看評論有人問了,所以補充個問題,就是atomic一定是執行緒安全的麼,回答是NO :

nonatomic的記憶體管理語義是非原子性的,非原子性的操作本來就是執行緒不安全,而atomic的操作是原子性的,但並不意味著他就是執行緒安全的,它會增加正確的機率,能夠更好的避免執行緒錯誤,但仍舊是不安全的。

為了說atomic與nonatomic的本質區別其實也就是在setter方法上的操作不同:

nonatomic的實現:

- (void)setCurrentImage:(UIImage *)currentImage

{

if (_currentImage != currentImage) {

[_currentImage release];

_currentImage = [currentImage retain];

// do something

}

}

- (UIImage *)currentImage

{

return _currentImage;

}

atomic的實現:

- (void)setCurrentImage:(UIImage *)currentImage

{

@synchronized(self) {

if (_currentImage != currentImage) {

[_currentImage release];

_currentImage = [currentImage retain];

// do something

}

}

}

- (UIImage *)currentImage

{

@synchronized(self) {

return _currentImage;

}

}

Using the @synchronized Directive

The @synchronized directive is a convenient way to create mutex locks on the fly in Objective-C code. The @synchronized directive does what any other mutex lock would do—it prevents different threads from acquiring the same lock at the same time. In this case, however, you do not have to create the mutex or lock object directly. Instead, you simply use any Objective-C object as a lock token, as shown in the following example:

- (void)myMethod:(id)anObj

{

@synchronized(anObj)

{

// Everything between the braces is protected by the @synchronized directive.

}

}

The object passed to the @synchronized directive is a unique identifier used to distinguish the protected block. If you execute the preceding method in two different threads, passing a different object for the anObj parameter on each thread, each would take its lock and continue processing without being blocked by the other. If you pass the same object in both cases, however, one of the threads would acquire the lock first and the other would block until the first thread completed the critical section.

As a precautionary measure, the @synchronized block implicitly adds an exception handler to the protected code. This handler automatically releases the mutex in the event that an exception is thrown. This means that in order to use the @synchronized directive, you must also enable Objective-C exception handling in your code. If you do not want the additional overhead caused by the implicit exception handler, you should consider using the lock classes.

For more information about the @synchronized directive, see The Objective-C Programming Language.

當使用atomic時,雖然對屬性的讀和寫是原子性的,但是仍然可能出現執行緒錯誤:當執行緒A進行寫操作,這時其他執行緒的讀或者寫操作會因為等該操作而等待。當A執行緒的寫操作結束後,B執行緒進行寫操作,所有這些不同執行緒上的操作都將依次順序執行——也就是說,如果一個執行緒正在執行 getter/setter,其他執行緒就得等待。如果有執行緒C在A執行緒讀操作之前release了該屬性,那麼還會導致程式崩潰。所以僅僅使用atomic並不會使得執行緒安全,我們還要為執行緒新增lock來確保執行緒的安全。

更準確的說應該是讀寫安全,但並不是執行緒安全的,因為別的執行緒還能進行讀寫之外的其他操作。執行緒安全需要開發者自己來保證。

其實無論是否是原子性的只是針對於getter和setter而言,比如用atomic去操作一個NSMutableArray ,如果一個執行緒迴圈讀資料,一個執行緒迴圈寫資料,肯定會產生記憶體問題,這個就跟getter和setter就木有關係了。

相關文章