我们在声明属性的时候,往往在@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.不消耗性能,快速读写
网友评论