9

在 Objective-C 中,将原始类型的属性指定为 是否有意义nonatomic

我想知道这两个属性之间的区别:

@property (nonatomic) BOOL myBool;
@property BOOL myBool;
4

3 回答 3

25

答案在技术上是肯定的,它们是不同的,但实际上不是,除非你编写自己的访问器。

让我解释。对于对象指针属性,@property NSObject *foo如果您使用合成访问器,则生成的代码有一个明显而重要的区别。这在Apple 文档中进行了描述,其中指出如果属性是原子的,合成访问器将锁定对象(如果您不指定非原子,则默认情况下它变为原子)

所以对于对象属性 简而言之:非原子更快但不是线程安全的,原子(你不能指定它,但它是默认值)是线程安全的但可能更慢。

(注意:如果你习惯了 Java,你可以认为 using nonatomicas like not specified synchronized,而不是指定nonatomicas like specified synchronized。换句话说 atomic = synchronized)

但是一个 BOOL 是一个原始的 - 实际上是一个 C 签名的字符,所以访问应该是原子的,而不像 Jano 的回答中提到的那样锁定对象。因此,当您合成访问器时,有两种可能性:1:编译器很聪明并且看到属性是原始的并避免锁定它,以及 2:编译器总是锁定原子属性的对象

据我所知,这在任何地方都没有记录,所以我尝试使用 XCode 中的 Generate->Assembly 选项并进行比较。答案并不完全确定,但足够接近可以说我几乎可以肯定答案是#1,编译器很聪明。我这样说是因为为原子对象属性生成的汇编代码与为非原子对象属性生成的汇编代码有很大不同(更多):这是锁定对象的所有代码。另一方面,对于 BOOL 属性,只有一行不同 - 一个“mov”,看起来不会有什么不同。我还是想知道。有趣的是,另一个区别是 BOOL 的原子版本有一些额外的注释大纲用于调试 - 所以编译器显然以不同的方式对待它。

尽管如此,相似之处在于我会说它们在实际用途中是相同的。

但是,如果您看不到实现,它们在技术上仍然不同,并且在您正在阅读的其他一些库(您没有自己编写代码)中可能存在实质性不同,原因如下:原子属性有一个contract。合同说:“如果你在多个线程上访问我的值,我保证每个设置或获取操作将在任何其他操作开始之前完成”。

但是,你说,BOOL 仍然是自然原子的,那么这个契约不是隐含的吗?

不。 BOOL变量自然是原子的,但我们谈论的是属性。一个属性可能不会被合成,甚至可能没有一个变量来支持它。这实际上很常见。考虑:

@property (getter=isEmptyThingo) BOOL emptyThingo;

...
- (BOOL)isEmptyThingo
{
    Thingo *thingo = [self deriveTheThingo];
    if ([thingo length] == 0) {
        return YES;
    }
    return NO;
}

谁知道里面发生了什么deriveTheThingo!?好的,这有点做作,但关键是 isEmptyThingo - 我们的 getter 看起来不是很原子,是吗?如果一个线程正在派生thingo,而另一个线程调用来查找它是否为空,会发生什么情况。

长话短说:该属性不是原子的。所以我们应该这样声明。

因此 m 原始答案合格:如果您自己编写此属性并使用@synthesize,那么它们可能是相同的,但您通常不应将它们视为相同。

根据经验,如果您不需要多线程支持——如果您使用 UIViewControllers 之类的 UI 代码通常不需要,那么只需将其声明为非原子的。

于 2012-10-31T11:04:27.227 回答
8

在 x 位架构(例如:32 位、64 位等)中,任何 x 位或更少位的值将始终以原子方式读取或写入。这是任何健全的硬件实现的属性。

默认原子属性意味着始终设置或获取一个属性值,而不管其他线程在做什么。这只是对超过架构位数的属性的关注。编译器在任何其他类型上都完全忽略了非原子。

例子:

@property struct { int d; } flag;
@property (atomic) struct { float x; float y; } point;
@property (atomic,copy) NSString *s;
  • struct { int d; }已经是原子的,因此访问者不需要互斥。

  • struct { float x, float y}如果它不是原子的,则可能处于不一致的状态。示例:两个线程设置{1,2}并且{3,4}可能重叠写入,并且结构可能以每个集合的值结束:{1,4}.

  • 指针存储在单个内存位置,但它们需要多个语句来进行内存管理。

原子属性有助于线程安全,避免导致值不一致或内存管理事故的竞争条件。仅此一项并不能保证线程安全,因为它不处理其他问题,如死锁、饥饿、可见性等。

于 2012-10-17T15:58:17.603 回答
2

是的。nonatomic不是内存管理关键字,它与线程安全有关。此外,默认情况下属性是原子的(没有明确声明它们是非原子的),因此您列出的两个声明之间存在差异

于 2012-10-17T15:55:20.370 回答