原始类型的Objective-c属性

时间:2012-10-17 15:53:44

标签: objective-c properties nonatomic

在Objective-C中将基本类型的属性指定为nonatomic是否有意义?

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

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

3 个答案:

答案 0 :(得分:25)

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

让我解释一下。对于对象指针属性,比如@property NSObject *foo,如果使用合成访问器,则生成的代码中存在明显且重要的差异。这在Apple documentation中有描述,它指出如果属性是原子的,合成访问器将锁定对象(如果你没有指定nonatomic,它默认为原子)< / p>

因此对于对象属性在一个非常粗略的概述:非原子是更快但不是线程安全,原子(你不能指定它,但它的默认值)是线程安全但可能更慢。

(注意:如果您习惯使用Java,则可以考虑使用nonatomic,而不是而不是指定synchronized,而不是指定nonatomic就像指定synchronized一样。换句话说,atomic = synchronized)

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

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

尽管如此,我认为它们在实际应用中是相同的。

但是它们在技术上仍然存在差异,如果你看不到实现,那么你正在阅读的其他一些库(你自己没有编写代码)可能会有很大的不同,这就是原因: atomic < / em>属性具有合同。合同说:“如果您在多个线程上访问我的值,我保证每个设置或获取操作将在任何其他线程开始之前完成”。

但是,你说,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而另一个线程调用以查找它是否为空,会发生什么。

长话短说:财产不是原子的。所以我们应该这样宣布。

因此,原始答案合格:如果你自己编写这个属性并使用@synthesize,那么它们可能是相同的,但你通常不应该对它们进行同样的处理。

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

答案 1 :(得分:8)

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

默认的原子属性意味着始终设置或获取属性值,而忽略其他线程正在执行的操作。这只是超出体系结构位数的属性的一个问题。编译器在任何其他类型上完全忽略Nonatomic。

示例:

@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}

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

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

答案 2 :(得分:2)

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