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.不消耗性能,快速读写

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