我們聲明屬性的時候,必須要考慮到屬性特質對編譯器所產生的存取方法的影響。
下面我們就主要講講atomic 與 nonatomic:
在默認情況下,由編譯器所合成的方法會通過鎖定機制確保其原子性(atomicity)。如果屬性具備nonatomic特質,則不需要同步鎖。
請注意?。。。”M管沒有名為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就木有關系了。