我們聲明屬性的時候衙伶,必須要考慮到屬性特質(zhì)對編譯器所產(chǎn)生的存取方法的影響。
下面我們就主要講講atomic 與 nonatomic:
在默認(rèn)情況下,由編譯器所合成的方法會通過鎖定機制確保其原子性(atomicity)。如果屬性具備nonatomic特質(zhì),則不需要同步鎖瞧毙。
請注意!<闹ⅰIぁ!盡管沒有名為atomic的特質(zhì)(如果某屬性不具備nonatomic特質(zhì)瘸爽,那它就是“原子的”(atomic))您访,但是仍然可以在屬性特質(zhì)中寫明這一點,編譯器是不會報錯的剪决。
那么atomic與nonatomic的區(qū)別又是什么捏灵汪?
之前說過滴檀训,具備atomic特質(zhì)的獲取方法會通過鎖定機制來確保其操作的原子性。
也就是說享言,如果兩個線程同時讀取一個屬性峻凫,那么不論何時,總能看到有效的屬性值览露。
如果不加鎖的話(或者說使用nonatomic語義)荧琼,那么當(dāng)其中一個線程正在改寫某屬性值的時候,另外一個線程也許會突然闖入差牛,把尚未修改好的屬性值讀取出來命锄。發(fā)證這種情況時,線程讀取道德屬性值肯能不對偏化。
相信大家都遇到過上述那種情況吧脐恩。。侦讨。驶冒。
一般iOS程序中,所有屬性都聲明為nonatomic韵卤。這樣做的原因是:
在iOS中使用同步鎖的開銷比較大骗污, 這會帶來性能問題。一般情況下并不要求屬性必須是“原子的”沈条,因為這并不能保證“線程安全”(thread safety)身堡,若要實現(xiàn)“線程安全”的操作,還需采用更為深層的鎖定機制才醒拍鲤。
例如:一個線程在連續(xù)多次讀取某個屬性值的過程中有別的線程在同時改寫該值,那么即便將屬性聲明為atomic汞扎,也還是會讀取到不同的屬性值季稳。
因此,iOS程序一般都會使用nonatomic屬性澈魄。但是在Mac OS X程序時景鼠, 使用atomic屬性通常都不會有性能瓶頸
因為看評論有人問了,所以補充個問題痹扇,就是atomic一定是線程安全的么铛漓,回答是NO :
nonatomic的內(nèi)存管理語義是非原子性的,非原子性的操作本來就是線程不安全鲫构,而atomic的操作是原子性的浓恶,但并不意味著他就是線程安全的,它會增加正確的幾率结笨,能夠更好的避免線程錯誤包晰,但仍舊是不安全的湿镀。
為了說atomic與nonatomic的本質(zhì)區(qū)別其實也就是在setter方法上的操作不同:
nonatomic的實現(xiàn):
- (void)setCurrentImage:(UIImage *)currentImage
{
if (_currentImage != currentImage) {
[_currentImage release];
_currentImage = [currentImage retain];
// do something
}
}
- (UIImage *)currentImage
{
return _currentImage;
}
atomic的實現(xiàn):
- (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.
當(dāng)使用atomic時,雖然對屬性的讀和寫是原子性的伐憾,但是仍然可能出現(xiàn)線程錯誤:當(dāng)線程A進行寫操作勉痴,這時其他線程的讀或者寫操作會因為等該操作而等待。當(dāng)A線程的寫操作結(jié)束后树肃,B線程進行寫操作蒸矛,所有這些不同線程上的操作都將依次順序執(zhí)行——也就是說,如果一個線程正在執(zhí)行 getter/setter胸嘴,其他線程就得等待雏掠。如果有線程C在A線程讀操作之前release了該屬性,那么還會導(dǎo)致程序崩潰筛谚。所以僅僅使用atomic并不會使得線程安全磁玉,我們還要為線程添加lock來確保線程的安全。
更準(zhǔn)確的說應(yīng)該是讀寫安全驾讲,但并不是線程安全的蚊伞,因為別的線程還能進行讀寫之外的其他操作。線程安全需要開發(fā)者自己來保證吮铭。
其實無論是否是原子性的只是針對于getter和setter而言时迫,比如用atomic去操作一個NSMutableArray ,如果一個線程循環(huán)讀數(shù)據(jù)谓晌,一個線程循環(huán)寫數(shù)據(jù)掠拳,肯定會產(chǎn)生內(nèi)存問題,這個就跟getter和setter就木有關(guān)系了纸肉。