美文网首页iOS开发面试Objective-C
[爆栈热门 iOS 问题] atomic 和 nonatomic

[爆栈热门 iOS 问题] atomic 和 nonatomic

作者: 戴仓薯 | 来源:发表于2015-10-09 15:33 被阅读13204次

系列文集:爆栈热门 iOS 问题目录在此。仓薯翻译,欢迎指正:)

问题

在定义 property 的时候,atomicnonatomic 有何区别?

@property(nonatomic, retain) UITextField *userName;
@property(atomic, retain) UITextField *userName;
@property(retain) UITextField *userName;

这仨有什么不同?


答案

答案1:理论和例子

bbum,1277 赞

后两行是一样的,不写的话默认就是atomic

atomic 和 nonatomic 的区别在于,系统自动生成的 getter/setter 方法不一样。如果你自己写 getter/setter,那 atomic/nonatomic/retain/assign/copy 这些关键字只起提示作用,写不写都一样。

对于atomic的属性,系统生成的 getter/setter 会保证 get、set 操作的完整性,不受其他线程影响。比如,线程 A 的 getter 方法运行到一半,线程 B 调用了 setter:那么线程 A 的 getter 还是能得到一个完好无损的对象。

nonatomic就没有这个保证了。所以,nonatomic的速度要比atomic快。

不过atomic可并不能保证线程安全。如果线程 A 调了 getter,与此同时线程 B 、线程 C 都调了 setter——那最后线程 A get 到的值,3种都有可能:可能是 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同时,最终这个属性的值,可能是 B set 的值,也有可能是 C set 的值。

保证数据完整性——这个多线程编程的最大挑战之一——往往还需要借助其他手段。


raw3d,66 赞

Atomic

  • 是默认的
  • 会保证 CPU 能在别的线程来访问这个属性之前,先执行完当前流程
  • 速度不快,因为要保证操作整体完成

Non-Atomic

  • 不是默认的
  • 更快
  • 线程不安全
  • 如有两个线程访问同一个属性,会出现无法预料的结果

Vijayendra Tripathi,57 赞

假设有一个 atomic 的属性 "name",如果线程 A 调[self setName:@"A"],线程 B 调[self setName:@"B"],线程 C 调[self name],那么所有这些不同线程上的操作都将依次顺序执行——也就是说,如果一个线程正在执行 getter/setter,其他线程就得等待。因此,属性 name 是读/写安全的。

但是,如果有另一个线程 D 同时在调[name release],那可能就会crash,因为 release 不受 getter/setter 操作的限制。也就是说,这个属性只能说是读/写安全的,但并不是线程安全的,因为别的线程还能进行读写之外的其他操作。线程安全需要开发者自己来保证。

如果 name 属性是 nonatomic 的,那么上面例子里的所有线程 A、B、C、D 都可以同时执行,可能导致无法预料的结果。如果是 atomic 的,那么 A、B、C 会串行,而 D 还是并行的。


答案2:用背后的代码来解释

苹果的官方文档 有解释了,下面我们举例子解释一下背后的原理。

//@property(nonatomic, retain) UITextField *userName;
//系统生成的代码如下:

- (UITextField *) userName {
    return userName;
}

- (void) setUserName:(UITextField *)userName_ {
    [userName_ retain];
    [userName release];
    userName = userName_;
}

而 atomic 版本的要复杂一些:

//@property(retain) UITextField *userName;
//系统生成的代码如下:

- (UITextField *) userName {
    UITextField *retval = nil;
    @synchronized(self) {
        retval = [[userName retain] autorelease];
    }
    return retval;
}

- (void) setUserName:(UITextField *)userName_ {
    @synchronized(self) {
      [userName release];
      userName = [userName_ retain];
    }
}

简单来说,就是 atomic 会加一个锁来保障线程安全,并且引用计数会 +1,来向调用者保证这个对象会一直存在。假如不这样做,如有另一个线程调 setter,可能会出现线程竞态,导致引用计数降到0,原来那个对象就释放掉了。

Jonathan Sterling 评论:要注意那个锁并不能『保证线程安全』。

原文地址:What's the difference between the atomic and nonatomic attributes?

本文地址:http://www.jianshu.com/p/7288eacbb1a2

系列文集:爆栈热门 iOS 问题

译者:@戴仓薯

相关文章

网友评论

  • Simon_H:我的理解是在atomic下,当有A、B、C三个线程同时操作时,线程的顺序如果没有人为去调度的话,则setter或getter的顺序是不确定的,即线程不安全,但重点是读/写是安全的,不会出现脏数据。
    但是在nonatomic下,在读写时可能会出现脏数据。
  • 2403345cab4b:薯哥, 我最近刚好在看锁相关的内容, synchronized这个是一个互斥锁, 我在其他文章中有看到, atomic原子性下的, 内部是一个自旋锁, 这两者是有区别的吧, 可否帮我解惑一下
    2403345cab4b:@戴仓薯 哈哈, 骚瑞啦, 没注意性别, 谢谢鼠妹回复XD
    戴仓薯:你说得对,确实可能有区别哒,用 synchronized 只是写法比较简单的一个示例~~ 对于各种锁的具体区别有很多文章,比如 https://www.jianshu.com/p/938d68ed832c

    另外,可以叫我薯妹哈……
  • liuyanhongwl:说个题外话问题:
    - (void) setUserName:(UITextField *)userName_ {
    @synchronized(self) {
    [userName release];
    userName = [userName_ retain];
    }
    }

    这里应该先retain 再release。
    如果userName新值旧值是一个对象,先release,可能导致系统将对象永久释放。
    2403345cab4b:setter里面应该还有一个if判断,
    if (userName != userName_) {}
  • ChiCoChiCo:写得真好,收藏啦
  • 不上火喝纯净水:简单说来,atomic保证了针对这个属性的成员变量的读写的原子性(读写过程中不会被其他线程乱入,其他线程的读写操作会阻塞等待);
    但是对于你的程序某段逻辑代码来说,
    1.xxxxx;
    2.write a=5; //写入因为是原子性的a=5这个setter过程必然是成功的
    3.xxxxx;
    4.read a; //因为步骤2之后和步骤4之前,a变量可能被其他线程读写,所以此处可能读取到意想不到的值

    也就是说这段代码是在某个线程中执行,但它不具有原子性,我们仍然需要自己使用同步机制来保证这段代码逻辑的正确性;

    是这样理解的吗
  • MoussyL:有个疑问,答案一最后一段:
    “不过atomic可并不能保证线程安全。如果线程 A 调了 getter,与此同时线程 B 、线程 C 都调了 setter——那最后线程 A get 到的值,3种都有可能:可能是 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同时,最终这个属性的值,可能是 B set 的值,也有可能是 C set 的值。”
    不太理解,既然说了atomic是读/写意义上的线程安全,难道结果不应该是,A调getter,之后才能轮到B线程、C线程吗? 为什么A get到的会是3种可能的值。。。
    hanryChen:@Zason_Hao 就是字面上的意思,在多线程里,B和C修改了属性,A去取这个属性,假如这些操作异步同时进行,则B和C都有可能是最后一个修改属性的。这个时候A取的值就不能保证是哪个,这就是atomic不能保证线程安全的原因。这个时候就需要再加一个栅栏函数保证这个属性是你要取的属性
    2ad4d4833970:这里的atomic应该是写错了,应该是nonatomic
    06f2f16a28bc:是的,这和之前描述矛盾了
  • Vikram_Lee:写的不错,收藏了
  • 七夜98:有个问题想要咨询一下,我们在写属性的时候,大多数情况下都是用nonatomic,而苹果默认却是atomic,不知道苹果这样做的用意是在哪儿呢?
    CNMD_LJ:。。因为你大多数情况下都是在主线程用啊,并不存在并发问题,所以用nonatomic,
    MoussyL:@戴仓薯 又长知识了,楼主果然是大神,SegementFault上经常看到你,给你点个大大的👍
    戴仓薯:@清风飞杨 理论上如果没有特殊要求的话, iOS 用 nonatomic 比较好,而 OSX 用 atomic 比较好。如果不写的话,系统按照相对比较安全的 atomic 处理。iOS 用 nonatomic 主要是出于性能考虑,OSX 上性能不是瓶颈,所以就不用了。
  • 托尼的夏天: In a sense, an atomic property is localized thread safety

本文标题:[爆栈热门 iOS 问题] atomic 和 nonatomic

本文链接:https://www.haomeiwen.com/subject/gnuncttx.html