nonatomic和atomic理論以及案例

我們在聲明屬性的時候,往往在@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.不消耗性能,快速讀寫

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章