在属性声明中,原子和非原子意味着什么?

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

这三者之间的操作差异是什么?


当前回答

原子:

原子保证以原子的方式访问财产。例如,它总是返回一个完全初始化的对象,一个线程上的属性的任何获取/设置都必须在另一个线程访问它之前完成。

如果你想象一下下面的函数同时出现在两个线程上,你就会明白为什么结果不好看。

-(void) setName:(NSString*)string
{
  if (name)
  {
    [name release]; 
    // what happens if the second thread jumps in now !?
    // name may be deleted, but our 'name' variable is still set!
    name = nil;
  }

  ...
}

赞成的意见:每次返回完全初始化的对象是多线程情况下的最佳选择。

欺骗:性能受到影响,执行速度稍慢

非原子:

与Atomic不同,它不能确保每次都返回完全初始化的对象。

赞成的意见:执行速度极快。

欺骗:在多线程的情况下,可能会产生垃圾值。

其他回答

在单行中:

原子是线程安全的。非原子是线程不安全的。

默认值是原子的,这意味着无论何时使用该属性都会降低性能,但它是线程安全的。Objective-C的作用是设置一个锁,因此只要执行setter/getter,只有实际线程才能访问变量。

具有ivar_internal的属性的MRC示例:

[_internal lock]; //lock
id result = [[value retain] autorelease];
[_internal unlock];
return result;

因此,后两项相同:

@property(atomic, retain) UITextField *userName;

@property(retain) UITextField *userName; // defaults to atomic

另一方面,非原子对代码没有任何影响。所以只有当你自己编写安全机制时,它才是线程安全的。

@property(nonatomic, retain) UITextField *userName;

关键字根本不必写为第一个属性属性。

别忘了,这并不意味着整个属性是线程安全的。只有setter/getter的方法调用是。但是如果您使用setter,然后同时使用2个不同线程的getter,那么它也可能会被破坏!

这个问题的其他优秀答案已经定义了语法和语义。因为执行和性能没有很好地详细描述,我将补充我的答案。

这三者之间的功能差异是什么?

我一直认为原子是一个默认值,这很奇怪。在我们工作的抽象级别上,使用类的原子财产作为实现100%线程安全的工具是一个很难的情况。对于真正正确的多线程程序,几乎肯定需要程序员的干预。同时,性能特征和执行还没有详细描述。多年来,我编写了一些大量的多线程程序,一直在声明我的财产是非原子的,因为原子对于任何目的都是不敏感的。在讨论原子和非原子财产这个问题的细节时,我做了一些分析,遇到了一些奇怪的结果。

处决

好的。我首先要澄清的是,锁定实现是由实现定义和抽象的。Louis在他的示例中使用了@synchronized(self)——我认为这是一个常见的混淆来源。该实现实际上没有使用@synchronized(self);它使用对象级自旋锁。Louis的插图很适合使用我们都熟悉的结构进行高级插图,但重要的是要知道它没有使用@synchronized(self)。

另一个区别是,原子财产将在getter中保留/释放对象的循环。

表演

这里有一个有趣的部分:在无竞争(例如单线程)的情况下,使用原子属性访问的性能在某些情况下会非常快。在不太理想的情况下,使用原子访问的开销可能是非原子访问的20倍以上。而使用7个线程的Contested情况下,三字节结构(2.2 GHz Core i7四核,x86_64)的速度慢了44倍。三字节结构是一个非常慢的属性的示例。

有趣的补充说明:三字节结构的用户定义访问器比合成原子访问器快52倍;或合成非原子存取器速度的84%。

在有争议的情况下,物体也可以超过50倍。

由于实现中的优化和变化的数量,很难在这些环境中测量真实世界的影响。你可能经常听到这样的话:“相信它,除非你分析并发现它是个问题”。由于抽象级别的原因,实际上很难衡量实际影响。从概要文件中提取实际成本可能非常耗时,而且由于抽象,非常不准确。同样,ARC与MRC可以产生巨大的差异。

因此,让我们后退一步,不关注属性访问的实现,我们将包括像objc_msgSend这样的常见问题,并在无争议的情况下(以秒为单位的值)检查对NSString getter的许多调用的一些实际高级结果:

MRC|非原子|手动实现的getter:2MRC|非原子|合成吸气剂:7MRC|原子|合成吸气剂:47ARC |非原子|合成吸气剂:38(注意:ARC在此添加了循环引用计数)ARC |原子|合成吸气剂:47

正如你可能已经猜到的,引用计数活动/循环是原子论和ARC下的一个重要贡献。你也会在有争议的案例中看到更大的差异。

虽然我非常关注性能,但我还是说Semantics First!。同时,性能对于许多项目来说是一个低优先级。然而,了解您所使用的技术的执行细节和成本当然不会有什么坏处。你应该根据自己的需要、目的和能力使用正确的技术。希望这将节省您几个小时的比较,并帮助您在设计程序时做出更明智的决定。

原子(默认)默认为Atomic:如果不键入任何内容,则属性为原子的如果您尝试从它,您将返回一个有效值。它不做任何保证关于这个值可能是什么,但你会得到好的数据,而不是只是垃圾记忆。如果您有多个线程或多个进程指向一个变量,一个线程可以读取,另一个线程可以写入。如果他们同时命中时间,读取器线程保证获得以下两个值之一:在改变之前或改变之后。什么原子没有给你任何形式的保证可能会得到。原子通常与线程安全相混淆,这是不正确的。你需要保证你的线程安全其他方式。然而,atomic将保证,如果您尝试阅读,你会得到某种价值。非原子的另一方面,非原子的,正如你可能猜到的,只是意味着,“不要做原子的事情。”你失去的是保证你总是要得到一些东西。如果你试着在写,你可以取回垃圾数据。但另一方面,你去快一点。因为原子财产必须发挥一些魔力为了保证你会得到一个值,它们有点慢。如果这是一个你经常访问的财产,你可能想放弃降到非原子,以确保你不会导致那样的速度处罚

在此处查看更多信息:https://realm.io/news/tmi-objective-c-property-attributes/

原子:

原子保证以原子的方式访问财产。例如,它总是返回一个完全初始化的对象,一个线程上的属性的任何获取/设置都必须在另一个线程访问它之前完成。

如果你想象一下下面的函数同时出现在两个线程上,你就会明白为什么结果不好看。

-(void) setName:(NSString*)string
{
  if (name)
  {
    [name release]; 
    // what happens if the second thread jumps in now !?
    // name may be deleted, but our 'name' variable is still set!
    name = nil;
  }

  ...
}

赞成的意见:每次返回完全初始化的对象是多线程情况下的最佳选择。

欺骗:性能受到影响,执行速度稍慢

非原子:

与Atomic不同,它不能确保每次都返回完全初始化的对象。

赞成的意见:执行速度极快。

欺骗:在多线程的情况下,可能会产生垃圾值。