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

时间:2021-07-31 02:24:12

In my few years as an iOS developer I don't think I've ever used atomic on a property. If I can see potential race conditions or data integrity issues due to threading, using atomic on a @property wouldn't ever help. I use conventional transaction/unit-of-work thread safety techniques (using mechanisms locks, semaphores or whatever).

在我作为iOS开发人员的几年里,我不认为我曾经在一个属性上使用过atomic。如果我能看到由于线程导致的潜在竞争条件或数据完整性问题,那么在@property上使用atomic将无济于事。我使用传统的事务/工作单元线程安全技术(使用机制锁,信号量或其他)。

Does anyone have (or know of) any practical examples of where atomic is used? (I'd love to see some actual/practical code examples)

有没有人(或知道)使用原子的任何实际例子? (我很想看到一些实际/实用的代码示例)

After writing nonatomic for maybe the billionth time, I'm also wondering why Apple have decided to make atomic the default.

在写了非原子可能的第十亿次之后,我也想知道为什么Apple决定将原子设为默认值。

5 个解决方案

#1


21  

As for the first problem you're having, maybe it's because

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

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.

虽然“原子”意味着对属性的访问是线程安全的,但只是让你的类中的所有属性都是原子的并不意味着你的类或更一般的对象图是“线程安全的” - 线程安全无法在关卡中表达个人访问方法。

As for why apple makes it atomic by default, I don't think there is any good reason, it was simply a bad design decision. Guys at the WWDC sessions repeatedly encouraged people to use nonatomic wherever you can to eliminate the performance impact.

至于为什么苹果默认使其成为原子,我认为没有任何好的理由,这只是一个糟糕的设计决定。 WWDC会议上的各位一再鼓励人们尽可能使用非原子技术来消除性能影响。

#2


10  

It's worth noting that under garbage collection, almost all synthesized accessors are inherently atomic—there would be no difference between the atomic and nonatomic version, since the simple assignment of a pointer is all that's required in both cases. Since you really can't make a nonatomic synthesized accessor under garbage collection, they may have decided that it made more sense to just have things atomic by default.

值得注意的是,在垃圾收集下,几乎所有合成访问器本质上都是原子的 - 原子和非原子版本之间没有区别,因为指针的简单分配就是两种情况下都需要的。既然你真的不能在垃圾收集下制作一个非原子合成访问器,他们可能已经决定默认情况下将事物设为原子更有意义。

I don't have any evidence that this was the reasoning behind the decision, but it makes sense to me.

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

(In case you're curious, there are still cases under garbage collection where simple assignment is nonatomic—this happens when the value is larger than the word size of the process. In practice, that only happens with structs.)

(如果你很好奇,仍然存在垃圾收集的情况,其中简单的赋值是非原子的 - 当值大于进程的字大小时会发生这种情况。实际上,这只发生在结构上。)

Edit: Added sources

编辑:添加来源

More information on the atomicity of synthesized properties under garbage collection can be found in The Objective-C Programming Language -> Declared Properties -> Performance and Threading, where it says "In a garbage collected environment, most synthesized methods are atomic without incurring this overhead." The inherent atomicity is mentioned more explicitly in the 2008 WWDC session 420 "Using Garbage Collection with Objective-C", around the 29 minute mark.

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

#3


4  

In two words - thread safety. Most of the applications we write on a regular basis are fairly simple and as such are actually going to benefit from not having the additional locks.

用两个词 - 线程安全。我们定期编写的大多数应用程序都非常简单,因此实际上可以从没有额外锁定中受益。


From Apple's The Objective-C Programming Language:

来自Apple的Objective-C编程语言:

Properties are atomic by default so that synthesized accessors provide robust access to properties in a multi-threaded environment—that is, the value returned from the getter or set via the setter is always fully retrieved or set regardless of what other threads are executing concurrently. For more details, see “Performance and Threading.”

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

If you do not specify nonatomic, then in a reference counted environment a synthesized get accessor for an object property uses a lock and retains and autoreleases the returned value—the implementation will be similar to the following:

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

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

If you specify nonatomic, then a synthesized accessor for an object property simply returns the value directly.

如果指定nonatomic,则对象属性的合成访问器只是直接返回值。

#4


3  

When Apple first introduced the concept of properties, there was a big argument about whether atomic or nonatomic should be the default and the atomic people won.

当Apple首次引入属性概念时,有一个很大的争论,即原子或非原子是否应该是默认值,原子人是否会赢。

I think the reasoning is that in a threaded environment, unless the property is atomic, you can't guarantee that the pointer it returns is valid. A non atomic getter would be implemented something like this

我认为原因是在线程环境中,除非属性是原子的,否则不能保证它返回的指针是有效的。非原子吸气剂将被实现这样的东西

-(MyObj*) someProperty
{
     return someInstVar;
}

It's possible that some other thread could deallocate the object pointed to by someInstVar after the pointer has been placed in the register for returning but before the caller has had time to retain it. Even this is no good:

在将指针放入寄存器中以便在调用者有时间保留它之前,某些其他线程可能会释放someInstVar指向的对象。即便这样也不好:

-(MyObj*) someProperty
{
     return [[someInstVar retain] autorelease];
}

because some other thread could dealloc someInstVar just before the retain count is incremented.

因为其他一些线程可以在保留计数递增之前释放someInstVar。

The only way to safely return the pointer in a multithreaded environment is something like this:

在多线程环境中安全返回指针的唯一方法是这样的:

-(MyObj*) someProperty
{
     @synchronized(self)
     {
         return [[someInstVar retain] autorelease];
     }
}

and also to synchronise the setter too.

并且也同步设置器。

However, the problem with that is that the lock is very expensive (in reality they used something much lighter than @synchronized but it's still expensive) so everybody was using nonatomic anyway and just making all the properties atomic doesn't confer thread safety in general so other techniques are required anyway and they tend to obviate the problem I discussed above.

然而,问题是锁是非常昂贵的(实际上他们使用的东西比@synchronized轻得多,但它仍然很昂贵)所以每个人都使用非原子,只是使所有的属性原子不会给一般的线程安全所以无论如何都需要其他技术,它们往往会消除我上面讨论过的问题。

There are plenty of people who think the wrong decision was made about what the default should be, but it can't be changed now for backwards compatibility. I find myself typing nonatomic without even thinking now.

有很多人认为对默认应该是什么做出了错误的决定,但现在为了向后兼容性而无法改变。我发现自己在没有思考的情况下打字非原子。

#5


2  

Atomic calls are calls that cannot be interrupted. The entire call must be executed.

原子呼叫是无法中断的呼叫。必须执行整个调用。

Updating something like a shared counter variable would be something that could be atomic because you wouldn't want two processes trying to access the variable at the same time. The actions would need to be executed "atomically".

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

There is a lot of useful information in this SO post: Atomic vs nonatomic properties as to what the differences are and the thread safety issues of atomic vs nonatomic.

在这篇SO帖子中有很多有用的信息:原子与非原子属性关于差异是什么以及原子与非原子的线程安全问题。

#1


21  

As for the first problem you're having, maybe it's because

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

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.

虽然“原子”意味着对属性的访问是线程安全的,但只是让你的类中的所有属性都是原子的并不意味着你的类或更一般的对象图是“线程安全的” - 线程安全无法在关卡中表达个人访问方法。

As for why apple makes it atomic by default, I don't think there is any good reason, it was simply a bad design decision. Guys at the WWDC sessions repeatedly encouraged people to use nonatomic wherever you can to eliminate the performance impact.

至于为什么苹果默认使其成为原子,我认为没有任何好的理由,这只是一个糟糕的设计决定。 WWDC会议上的各位一再鼓励人们尽可能使用非原子技术来消除性能影响。

#2


10  

It's worth noting that under garbage collection, almost all synthesized accessors are inherently atomic—there would be no difference between the atomic and nonatomic version, since the simple assignment of a pointer is all that's required in both cases. Since you really can't make a nonatomic synthesized accessor under garbage collection, they may have decided that it made more sense to just have things atomic by default.

值得注意的是,在垃圾收集下,几乎所有合成访问器本质上都是原子的 - 原子和非原子版本之间没有区别,因为指针的简单分配就是两种情况下都需要的。既然你真的不能在垃圾收集下制作一个非原子合成访问器,他们可能已经决定默认情况下将事物设为原子更有意义。

I don't have any evidence that this was the reasoning behind the decision, but it makes sense to me.

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

(In case you're curious, there are still cases under garbage collection where simple assignment is nonatomic—this happens when the value is larger than the word size of the process. In practice, that only happens with structs.)

(如果你很好奇,仍然存在垃圾收集的情况,其中简单的赋值是非原子的 - 当值大于进程的字大小时会发生这种情况。实际上,这只发生在结构上。)

Edit: Added sources

编辑:添加来源

More information on the atomicity of synthesized properties under garbage collection can be found in The Objective-C Programming Language -> Declared Properties -> Performance and Threading, where it says "In a garbage collected environment, most synthesized methods are atomic without incurring this overhead." The inherent atomicity is mentioned more explicitly in the 2008 WWDC session 420 "Using Garbage Collection with Objective-C", around the 29 minute mark.

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

#3


4  

In two words - thread safety. Most of the applications we write on a regular basis are fairly simple and as such are actually going to benefit from not having the additional locks.

用两个词 - 线程安全。我们定期编写的大多数应用程序都非常简单,因此实际上可以从没有额外锁定中受益。


From Apple's The Objective-C Programming Language:

来自Apple的Objective-C编程语言:

Properties are atomic by default so that synthesized accessors provide robust access to properties in a multi-threaded environment—that is, the value returned from the getter or set via the setter is always fully retrieved or set regardless of what other threads are executing concurrently. For more details, see “Performance and Threading.”

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

If you do not specify nonatomic, then in a reference counted environment a synthesized get accessor for an object property uses a lock and retains and autoreleases the returned value—the implementation will be similar to the following:

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

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

If you specify nonatomic, then a synthesized accessor for an object property simply returns the value directly.

如果指定nonatomic,则对象属性的合成访问器只是直接返回值。

#4


3  

When Apple first introduced the concept of properties, there was a big argument about whether atomic or nonatomic should be the default and the atomic people won.

当Apple首次引入属性概念时,有一个很大的争论,即原子或非原子是否应该是默认值,原子人是否会赢。

I think the reasoning is that in a threaded environment, unless the property is atomic, you can't guarantee that the pointer it returns is valid. A non atomic getter would be implemented something like this

我认为原因是在线程环境中,除非属性是原子的,否则不能保证它返回的指针是有效的。非原子吸气剂将被实现这样的东西

-(MyObj*) someProperty
{
     return someInstVar;
}

It's possible that some other thread could deallocate the object pointed to by someInstVar after the pointer has been placed in the register for returning but before the caller has had time to retain it. Even this is no good:

在将指针放入寄存器中以便在调用者有时间保留它之前,某些其他线程可能会释放someInstVar指向的对象。即便这样也不好:

-(MyObj*) someProperty
{
     return [[someInstVar retain] autorelease];
}

because some other thread could dealloc someInstVar just before the retain count is incremented.

因为其他一些线程可以在保留计数递增之前释放someInstVar。

The only way to safely return the pointer in a multithreaded environment is something like this:

在多线程环境中安全返回指针的唯一方法是这样的:

-(MyObj*) someProperty
{
     @synchronized(self)
     {
         return [[someInstVar retain] autorelease];
     }
}

and also to synchronise the setter too.

并且也同步设置器。

However, the problem with that is that the lock is very expensive (in reality they used something much lighter than @synchronized but it's still expensive) so everybody was using nonatomic anyway and just making all the properties atomic doesn't confer thread safety in general so other techniques are required anyway and they tend to obviate the problem I discussed above.

然而,问题是锁是非常昂贵的(实际上他们使用的东西比@synchronized轻得多,但它仍然很昂贵)所以每个人都使用非原子,只是使所有的属性原子不会给一般的线程安全所以无论如何都需要其他技术,它们往往会消除我上面讨论过的问题。

There are plenty of people who think the wrong decision was made about what the default should be, but it can't be changed now for backwards compatibility. I find myself typing nonatomic without even thinking now.

有很多人认为对默认应该是什么做出了错误的决定,但现在为了向后兼容性而无法改变。我发现自己在没有思考的情况下打字非原子。

#5


2  

Atomic calls are calls that cannot be interrupted. The entire call must be executed.

原子呼叫是无法中断的呼叫。必须执行整个调用。

Updating something like a shared counter variable would be something that could be atomic because you wouldn't want two processes trying to access the variable at the same time. The actions would need to be executed "atomically".

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

There is a lot of useful information in this SO post: Atomic vs nonatomic properties as to what the differences are and the thread safety issues of atomic vs nonatomic.

在这篇SO帖子中有很多有用的信息:原子与非原子属性关于差异是什么以及原子与非原子的线程安全问题。