协慌网

登录 贡献 社区

原子和非原子属性之间有什么区别?

属性声明中的atomicnonatomic意味着什么?

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

这三者之间的运作区别是什么?

答案

最后两个是相同的; “atomic” 是默认行为( 请注意,它实际上不是关键字; 仅通过缺少nonatomic atomic 来指定 - 在最近版本的 llvm / clang 中添加了atomic作为关键字)。

假设你是 @synthesizing 方法实现,原子与非原子会改变生成的代码。如果您正在编写自己的 setter / getter,则 atomic / nonatomic / retain / assign / copy 仅仅是建议性的。 (注意:@synthesize 现在是最近版本的 LLVM 中的默认行为。也没有必要声明实例变量; 它们也会自动合成,并且会在其名称前加上_以防止意外直接访问)。

对于 “原子”,合成的 setter / getter 将确保始终从 getter 返回整个值或由 setter 设置,而不管任何其他线程上的 setter 活动。也就是说,如果线程 A 位于 getter 的中间,而线程 B 调用 setter,则实际可行的值 - 一个自动释放的对象,很可能 - 将返回给 A 中的调用者。

nonatomic ,没有这样的保证。因此, nonatomic比 “原子” 快得多。

什么 “原子” 就是做出关于线程安全的任何保证。如果线程 A 与线程 B 同时调用 getter 并且 C 调用具有不同值的 setter,则线程 A 可以获得返回的三个值中的任何一个 - 在调用任何 setter 之前的值或者传递给 setter 的任一值在 B 和 C 中。同样,对象可能最终得到 B 或 C 的值,无法分辨。

确保数据完整性 - 多线程编程的主要挑战之一 - 是通过其他方式实现的。

添加到此:

当多个依赖属性在起作用时,单个属性的atomicity性也不能保证线程安全。

考虑:

@property(atomic, copy) NSString *firstName;
 @property(atomic, copy) NSString *lastName;
 @property(readonly, atomic, copy) NSString *fullName;

在这种情况下,线程 A 可以通过调用setFirstName:然后调用setLastName:来重命名对象。同时,线程 B 可以在线程 A 的两个调用之间调用fullName ,并将接收与旧的姓氏一起使用的新名字。

要解决此问题,您需要一个事务模型 。即一些其他类型的同步和 / 或排除,允许在更新依赖属性时排除对fullName访问。

这在 Apple 的文档中有解释,但下面是实际发生的一些示例。请注意,没有 “atomic” 关键字,如果未指定 “nonatomic”,则属性为 atomic,但明确指定 “atomic” 将导致错误。

//@property(nonatomic, retain) UITextField *userName;
//Generates roughly

- (UITextField *) userName {
    return userName;
}

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

现在,原子变体有点复杂:

//@property(retain) UITextField *userName;
//Generates roughly

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

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

基本上,原子版本必须采取锁定以保证线程安全,并且还会碰撞对象的引用计数(以及自动释放计数以平衡它),以便保证对象存在对象,否则存在如果另一个线程正在设置该值,则是一个潜在的竞争条件,导致引用计数降至 0。

根据属性是标量值还是对象,以及如何保留,复制,只读,非原子等交互,实际上有很多不同的变体如何工作。一般来说,属性合成器只知道如何为所有组合做 “正确的事”。

原子

  • 是默认行为
  • 在另一个进程访问变量之前,将确保 CPU 完成当前进程
  • 并不快,因为它确保完整的过程完成

非原子

  • 不是默认行为
  • 更快(对于合成代码,即对于使用 @property 和 @synthesize 创建的变量)
  • 不是线程安全的
  • 当两个不同的进程同时访问同一个变量时,可能会导致意外行为