我們在聲明屬性的時候,往往在@property的括號中第一個就寫上nonatomic,但是nonatomic是什麼意思?又有什麼作用呢?
我們在搜索nonatomic和atomic的時候,很多人都會給你相似的答案,卻鮮少有人貼出代碼來驗證一下,我自認爲是個笨拙的人,不自己驗證一下總是不那麼放心。
先看看理論上nonatomic和atomic的不同吧。
atomic的英文意思是原子的。
我們姑且稱它爲原子性吧。它的作用應該就是爲了使得對象讀寫的時候更加安全而設立的。它們倆的最大的區別就在於系統生成的getter和setter方法不一樣。我們來看看吧。
@property(atomic, strong)UIImage * icon1;
@property(nonatomic, strong)UIImage * icon2;
nonatomic對象setter和getter方法的實現
//mrc 環境
//implementation
@synthesize icon2 = _icon2;
//set
-(void)setIcon2:(UIImage *)icon2
{
if(_icon2 != icon2)
{
[_icon2 release];
_icon2 = [icon2 retain];
}
}
//get
-(UIImage *)icon2
{
return _icon2;
}
atomic對象setter和getter方法的實現
//mrc 環境
//implementation
@synthesize icon1 = _icon1;
//set
-(void)setIcon1:(UIImage *)icon1
{
@synchronized (self) {
if(_icon1 != icon1)
{
[_icon1 release];
_icon1 = [icon1 retain];
}
}
}
//get
-(UIImage *)icon1
{
UIImage *image = nil;
@synchronized (self) {
image = [[_icon1 retain] autorelease];
}
return image;
}
擴展
@synchronized(對象)中的這個"對象"是什麼,鎖定它是爲了什麼? 這個對象可以是任意一個object,作用是以object的內存地址映射到系統維護的一個遞歸鎖;當獲得鎖之後,其他線程想進入同一段代碼時需要等待,其他線程想使用同一個object進行加鎖時也需要等待。
@synchronized (self)這種寫法會根據給定的對象,自動創建一個鎖,並等待塊中的代碼執行完畢。執行到這段代碼結尾處,鎖就釋放了。
我們現在明白了,atomic修飾的對象,在setter和getter方法中,都加了一個鎖,也就是在存取的時候,只有當存取已經完成了,其他線程才能夠訪問這個對象,這樣保證了這個對象在存取的時候是安全的。
我們用圖示來表示:
如此看來,atomic還是能夠保證存取的時候是安全的,那麼它是線程安全的嗎?不一定的。我們舉個例子試試。
例:
一個對象name = “hello”,有三個異步線程A、B、C,A中取name的值,B、C改變name的值,那麼最終,name的值爲多少?A中取到的又是哪一個值呢?
// Copyright © 2020 weiman. All rights reserved.
#import "ViewController.h"
@interface ViewController ()
@property(atomic,strong)NSString * name;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.name = @"hello";
NSBlockOperation *blockA = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"blockA- %@",[NSThread currentThread]);
NSLog(@"blockA name = %@", self.name);
}];
NSBlockOperation *blockB = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"blockB- %@",[NSThread currentThread]);
self.name = @"小橘子";
}];
NSBlockOperation *blockC = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"-blockC-%@",[NSThread currentThread]);
self.name = @"小土豆";
}];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
queue.maxConcurrentOperationCount = 3;
[queue addOperation:blockA];
[queue addOperation:blockB];
[queue addOperation:blockC];
}
@end
打印結果:
多運行幾次發現,打印結果並不固定,說明atomic並不能做到線程安全。因爲線程安全還有讀寫之外的其他操作(比如:如果當一個線程正在get或set時,又有另一個線程同時在進行release操作,可能會直接crash)。
因爲atomic的讀寫加鎖操作,會使得性能降低,讀寫變得耗時,所以我們在聲明屬性的時候基本不會使用它。
說完了atomic,我們就知道我們常用的nonatomic的意思了,就是讀寫不加鎖的操作。
總結
atomic:
1.系統默認的
2.讀寫安全的
3.線程不安全的
4.消耗性能的
nonatomic:
1.讀寫不安全
2.不消耗性能,快速讀寫