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

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

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


当前回答

原子财产(Atomic properties):-当一个变量被赋值为原子属性时,意味着它只有一个线程访问权限,并且它是线程安全的,从性能角度来看速度较慢,那么它将具有默认行为。

非原子财产:-当一个变量被赋值为非原子属性时,这意味着它具有多线程访问权限,并且它不是线程安全的,从性能角度来看速度很快,它将具有默认行为,并且当两个不同的线程要同时访问该变量时,它将产生意外的结果。

其他回答

原子属性确保保留完全初始化的值,而不管有多少线程在其上执行getter和setter。

非原子属性指定合成访问器只需直接设置或返回一个值,而不保证从不同线程同时访问同一个值会发生什么。

我在这里找到了原子和非原子财产的一个很好的解释。以下是一些相关文本:

“原子”意味着它不能分解。在OS/编程术语中,原子函数调用是一个不能中断的调用——必须执行整个函数,并且在完成之前,不能通过操作系统通常的上下文切换将其从CPU中移出。万一你不知道:由于CPU一次只能做一件事,操作系统会在很小的时间段内对所有正在运行的进程轮流访问CPU,从而产生多任务的错觉。CPU调度器可以(并且确实)在进程执行的任何时间点中断进程,即使是在函数调用中间。因此,对于像更新共享计数器变量这样的操作,如果两个进程可以同时尝试更新变量,则必须“原子”地执行这些操作,即,每个更新操作必须完整完成,然后才能将任何其他进程交换到CPU上。所以我猜测,在这种情况下,原子意味着属性读取器方法不能被中断,实际上意味着该方法读取的变量不能中途更改其值,因为其他线程/调用/函数被交换到CPU上。

因为原子变量不能被中断,所以它们在任何时候包含的值(线程锁)都保证不会被破坏,尽管确保这个线程锁会使对它们的访问变慢。另一方面,非原子变量不能保证这一点,但确实提供了快速访问的奢侈。总之,当您知道变量不会被多个线程同时访问时,可以使用非原子的,这样可以加快速度。

了解差异的最佳方法是使用以下示例。

假设有一个名为“name”的原子字符串属性,如果您从线程A调用[self-setName:@“A”],从线程B调用[selfsetName:@“B”],并从线程C调用[self name],那么不同线程上的所有操作都将串行执行,这意味着如果一个线程正在执行setter或getter,那么其他线程将等待。

这使得属性“name”读/写安全,但如果另一个线程D同时调用[namerelease],那么这个操作可能会产生崩溃,因为这里没有setter/getter调用。这意味着一个对象是读/写安全的(ATOMIC),但不是线程安全的,因为另一个线程可以同时向该对象发送任何类型的消息。开发人员应确保此类对象的线程安全。

如果属性“name”是非原子的,那么上面示例中的所有线程(A、B、C和D)将同时执行,产生任何不可预测的结果。在原子的情况下,A、B或C中的任何一个将首先执行,但D仍然可以并行执行。

原子:通过使用NSLOCK锁定线程来确保线程安全。

非原子:由于没有线程锁定机制,因此无法确保线程安全。

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

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

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