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

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

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


当前回答

首先最简单的回答是:第二个例子之间没有区别。默认情况下,属性访问器是原子的。

非垃圾收集环境中的原子访问器(即,当使用retain/release/autorelease时)将使用锁来确保另一个线程不会干扰值的正确设置/获取。

有关更多信息以及创建多线程应用程序时的其他注意事项,请参阅苹果Objective-C 2.0文档的“性能和线程”部分。

其他回答

苹果的文档中对此进行了解释,但下面是一些实际发生情况的示例。

请注意,没有“atomic”关键字,如果不指定“nonatomic(非原子)”,则属性是原子的,但显式指定“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。

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

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

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

我一直认为原子是一个默认值,这很奇怪。在我们工作的抽象级别上,使用类的原子财产作为实现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!。同时,性能对于许多项目来说是一个低优先级。然而,了解您所使用的技术的执行细节和成本当然不会有什么坏处。你应该根据自己的需要、目的和能力使用正确的技术。希望这将节省您几个小时的比较,并帮助您在设计程序时做出更明智的决定。

-原子意味着只有一个线程访问变量(静态类型)。-原子是线程安全的。-但它的性能很慢

如何申报:

由于原子是默认的,

@property (retain) NSString *name;

实现文件中的AND

self.name = @"sourov";

假设与三个财产相关的任务是

 @property (retain) NSString *name;
 @property (retain) NSString *A;
 @property (retain) NSString *B;
 self.name = @"sourov";

所有财产并行工作(如异步)。

如果从线程A调用“name”,

And

同时如果你打电话

[self setName:@"Datta"]

从线程B,

现在,如果*name属性是非原子的,那么

它将返回A的值“Datta”它将返回B的值“Datta”

这就是为什么非原子被称为线程不安全的原因,但由于并行执行,它的性能很快

现在如果*name属性是原子的

这将确保A的价值“Sourov”然后它将返回B的值“Datta”

这就是为什么原子被称为线程安全和这就是为什么它被称为读写安全

这种情况操作将连续执行。性能缓慢

-非原子意味着多线程访问变量(动态类型)。

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

-但它的性能很快

-非原子不是默认行为,我们需要在属性属性中添加非原子关键字。

对于In Swift确认Swift财产在ObjC意义上是非原子的。一个原因是,您需要考虑每个属性的原子性是否足以满足您的需求。

参考:https://forums.developer.apple.com/thread/25642

有关更多信息,请访问网站http://rdcworld-iphone.blogspot.in/2012/12/variable-property-attributes-or.html

原子:

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

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

-(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不同,它不能确保每次都返回完全初始化的对象。

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

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

在开始之前:您必须知道内存中的每个对象都需要从内存中解除分配,才能生成新的写入程序。你不能像在纸上那样简单地在上面写字。您必须首先擦除(解除锁定)它,然后才能写入它。如果在擦除完成(或完成一半)的时候,还没有写入任何内容(或写入一半),而您尝试读取它可能会非常困难!原子和非原子帮助你以不同的方式处理这个问题。

首先阅读这个问题,然后阅读Bbum的答案。此外,请阅读我的摘要。


原子将始终保证

如果两个不同的人想同时阅读和写作,你的论文就不会燃烧!-->即使在竞争条件下,您的应用程序也不会崩溃。如果一个人正在尝试写,并且只写了8个字母中的4个,那么没有人可以在中间读,只有当所有8个字母都写了之后才能进行读取-->“仍在写的线程”上不会发生读取(get),即如果要写入8个字节到字节,并且只写入了4个字节,到那时为止,您不允许从中读取。但既然我说它不会崩溃,那么它将从自动释放对象的值中读取。如果在写作之前,你已经删除了之前写在纸上的内容,然后有人想要阅读,你仍然可以阅读。怎样您将从类似于Mac OS垃圾箱的内容中读取内容(因为垃圾箱尚未被100%擦除……它处于边缘状态)--->如果ThreadA在ThreadB已解除分配写入时读取,您将从ThreadB的最终完全写入值中获得一个值,或者从自动释放池中获得一些值。

保留计数是Objective-C中管理内存的方式。创建对象时,其保留计数为1。当您发送对象为保留消息,其保留计数递增1。什么时候如果向对象发送释放消息,则其保留计数将递减通过1。向对象发送自动释放消息时,其保留计数在将来的某个阶段减1。如果对象保留计数减为0,则解除分配。

Atomic不能保证线程安全,但它对实现线程安全很有用。线程安全与您如何编写代码/从哪个线程队列读取/写入代码有关。它只保证不可崩溃的多线程。


什么多线程和线程安全不同吗?

对多线程意味着:多个线程可以同时读取一段共享数据,我们不会崩溃,但这并不能保证您不会从非自动释放的值读取数据。通过线程安全,可以保证您读取的内容不会自动释放。默认情况下,我们没有使所有内容都原子化的原因是,存在性能成本,而且大多数情况下并不真正需要线程安全。我们的代码有几个部分需要它,对于这几个部分,我们需要使用锁、互斥锁或同步以线程安全的方式编写代码。


非原子的

因为没有像Mac OS垃圾箱这样的东西,所以没有人关心你是否总是得到一个值(<--这可能会导致崩溃),也没有人会在意是否有人试图在你的写作过程中读到一半(虽然在内存中写的一半与在纸上写的一半有很大不同,但在内存中,它可能会给你一个前所未有的愚蠢值,而在纸上,你只能看到一半的内容)-->不保证不会崩溃,因为它不使用自动释放机制。不保证读取完整的写入值!比原子更快


总体而言,它们在两个方面有所不同:

崩溃或不是因为有或没有自动释放池。允许在“尚未完成写入或空值”的中间读取,或不允许且仅允许在值完全写入时读取。