当我发现自己100%使用非原子时,为什么“原子”是Objective C中的默认@property限定符?

Why is “atomic” a default @property qualifier in Objective C when I find myself using nonatomic 100% of the time?

在我作为iOS开发者的几年里,我不认为我曾经在一个属性上使用过原子。如果我能看到线程可能导致的竞争条件或数据完整性问题,那么在@property上使用原子不会有任何帮助。我使用传统事务/工作线程单元安全技术(使用机制锁、信号量或其他)。

有人(或知道)在哪里使用原子的任何实际例子吗?(我希望看到一些实际/实际的代码示例)

在写了第十亿次非原子的文章之后,我还想知道为什么苹果公司决定让原子成为默认值。


至于你遇到的第一个问题,也许是因为

Although"atomic" means that access to the property is thread-safe, simply making all the properties in your class atomic does not mean that your class or more generally your object graph is"thread safe"—thread safety cannot be expressed at the level of individual accessor methods.

至于苹果为什么默认为原子的,我认为没有什么好的理由,这只是一个糟糕的设计决定。WWDC会议上的人反复鼓励人们尽可能使用非原子的,以消除性能影响。


值得注意的是,在垃圾收集下,几乎所有合成的访问器都是原子的,原子版本和非原子版本之间没有区别,因为在这两种情况下,只需要简单地分配指针。因为在垃圾收集下您真的不能创建非原子合成的访问器,所以他们可能已经决定默认情况下只使用原子的访问器更为合理。

我没有任何证据表明这是决定背后的原因,但这对我来说是有道理的。

(在你好奇的情况下,仍然存在垃圾收集的情况,其中简单的赋值是非原子的,这在值大于进程的字长时发生。实际上,这只发生在结构上。

编辑:添加的源

关于垃圾收集下合成属性的原子性的更多信息,可以在Objective-C编程语言->声明的属性->性能和线程中找到,其中它说"在垃圾收集环境中,大多数合成方法都是原子的,不会产生这种开销。"这里提到了固有的原子性。ORE在2008年WWDC第420届会议上明确提出"使用垃圾收集与目标-C",大约29分钟的标志。


两个字-线程安全。我们定期编写的大多数应用程序都相当简单,因此实际上没有额外的锁会受益匪浅。

来自苹果的Objective-C编程语言:

默认情况下,属性是原子的,因此合成访问器在多线程环境中提供对属性的可靠访问,也就是说,通过setter从getter或set返回的值始终是完全检索或设置的,而不管其他线程同时执行什么。有关详细信息,请参阅"性能和线程"。

如果不指定非原子,则在引用计数的环境中,对象属性的合成get访问器使用锁,并保留和自动释放返回值,实现将类似于以下内容:

1
2
3
4
[_internal lock]; // lock using an object-level lock
id result = [[value retain] autorelease];
[_internal unlock];
return result;

如果指定非原子的,那么对象属性的合成访问器只直接返回值。


当苹果首次引入属性的概念时,关于EDCOX1×0或EDOCX1×1是否应该是默认的和原子人赢得了一个很大的争论。

我认为原因是在线程环境中,除非属性是原子的,否则您不能保证它返回的指针是有效的。一个非原子getter可以实现如下

1
2
3
4
-(MyObj*) someProperty
{
     return someInstVar;
}

在指针被放入寄存器以便返回,但在调用者有时间保留它之前,其他线程可能会取消分配someinstvar指向的对象。即使这样也不好:

1
2
3
4
-(MyObj*) someProperty
{
     return [[someInstVar retain] autorelease];
}

因为其他线程可以在保留计数增加之前释放someinstvar。

在多线程环境中安全返回指针的唯一方法如下:

1
2
3
4
5
6
7
-(MyObj*) someProperty
{
     @synchronized(self)
     {
         return [[someInstVar retain] autorelease];
     }
}

也可以同步设置器。

但是,问题是锁非常昂贵(实际上,它们使用的东西比@synchronized轻很多,但仍然很昂贵),所以不管怎样,每个人都在使用非原子的,只是使所有的属性原子化并不能保证线程的安全性,所以无论如何都需要其他的技术,而且它们往往会排除pr。我在上面讨论过的问题。

有很多人认为关于默认值应该是什么做出了错误的决定,但是现在不能为了向后兼容性而改变它。我发现自己在输入nonatomic,现在连想都没想。


原子调用是不能中断的调用。必须执行整个调用。

更新类似共享计数器变量的内容可能是原子的,因为您不希望两个进程同时访问该变量。这些行动需要"原子地"执行。

在这篇文章中有很多有用的信息:原子与非原子属性,关于原子与非原子的区别以及线程安全问题。