原始类型的Objective-c属性

时间:2022-04-17 12:57:39

In Objective-C Does it ever make sense to specify a property for a primitive type as nonatomic?

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

I'm wondering about the difference between these two properties:

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

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

3 个解决方案

#1


24  

The answer is technically YES they're different, but practically NO they're not, unless you code your own accessors.

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

Let me explain. For an object pointer property, say @property NSObject *foo there is a clear and important difference in the code that gets generated if you use synthesize the accessors. This is described in the Apple documentation where it points out that the synthesized accessors will lock the object if the property is atomic (if you don't specify nonatomic, it becomes atomic by default)

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

So for object properties In a very crude nutshell: nonatomic is faster but not threadsafe, atomic (you can't specify it, but its the default) is threadsafe but potentially slower.

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

(Note: if you're accustomed to Java, you can think of using nonatomic as like not specifying synchronized, and not specifying nonatomic as like specifying synchronized. In other words atomic = synchronized)

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

But a BOOL is a primitive - in fact a C signed char, so access should be atomic without locking the object as mentioned in Jano's answer. So when you're synthesizing the accessor, there are two possibilities: 1: the compiler is smart and sees that the property is primitive and avoids locking it, and 2: the compiler always locks the object for atomic properties

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

As far as I can see this is not documented anywhere, so I tried it by using the Generate->Assembly option in XCode and comparing it. The answer was not completely conclusive, but close enough to say that I'm almost certain the answer is #1, the compiler is smart. I say this, because the assembly code generated for an atomic object property is considerably different (more of it) than for a non-atomic object property: this is all the code for locking the object. For a BOOL property on the other hand, there was only one line different - a single "mov" which doesn't look like it could possibly make a difference. Still I wonder. Interestingly, another difference is that the atomic version of BOOL has some extra commented-outlines for debugging - so the compiler is clearly treating it differently.

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

Nonetheless the similarity is such that I would say they're the same for practical purposes.

尽管如此,我认为它们的相似之处在于实际目的。

But they're still technically different and may be substantively different in some other library you're reading (that you didn't code yourself) if you can't see the implementation, and here's why: atomic properties have a contract. The contract says: "If you access my value on multiple threads, I promise that each setting or getting operation will complete before any other begins".

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

But, you say, BOOL is still naturally atomic, so isn't this contract implicit?

但是,你说,BOOL仍然是自然原子的,所以这个合约是否隐含?

No. A BOOL variable is naturally atomic, but we're talking about a property. A property might not be synthesized and might not even have a single variable to back it up. This is actually pretty common. Consider:

不。BOOL变量自然是原子的,但我们谈的是属性。属性可能无法合成,甚至可能没有单个变量来备份它。这实际上很常见。考虑:

@property (getter=isEmptyThingo) BOOL emptyThingo;

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

who knows what's going on in deriveTheThingo!? Okay, this is a bit contrived, but the point is that isEmptyThingo - our getter doesn't look very atomic, does it? What happens if one thread is deriving the thingo and another thread comes calling to find if its empty.

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

Long story short: the property is not atomic. So we should declare it so.

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

Hence m original answer qualified: if you're writing this property yourself and using @synthesize, then they're probably the same, but you should generally not treat them the same.

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

As a rule of thumb, if you don't need multithreaded support - which you generally don't if you're working in UI code like UIViewControllers, then just declare it all nonatomic.

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

#2


8  

In a x-bit architecture (eg: 32bit, 64bit, etc.) any value which is x or less bits will always be read or written atomically. This is a property of any sane hardware implementation.

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

The default atomic property means that a property value is always set or get in whole, disregarding what other threads are doing. This is only a concern for properties that exceed the number of bits of the architecture. Nonatomic is completely ignored by the compiler on any other type.

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

Example:

@property struct { int d; } flag;
@property (atomic) struct { float x; float y; } point;
@property (atomic,copy) NSString *s;
  • struct { int d; } is already atomic so the accesors don't need mutual exclusion.

    struct {int d;已经是原子的,所以加法器不需要互斥。

  • struct { float x, float y} could be in an inconsistent state if it wasn't atomic. Example: two threads setting {1,2} and {3,4} could overlap the writing, and the struct could end up with a value from each set: {1,4}.

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

  • Pointers are stored in a single memory position but they require several statements for memory management.
  • 指针存储在单个内存位置,但它们需要多个语句来进行内存管理。

Atomic properties contribute to thread safety avoiding race conditions that lead to inconsistent values or memory management mishaps. This alone doesn't guarantee thread safety because it doesn't deal with other problems like deadlock, starvation, visibility, etc.

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

#3


2  

Yes. nonatomic is not a memory-management keyword, it has to do with thread safety. Also, properties are atomic by default (without explicitly declaring them as nonatomic), so there is a difference between the two declarations you listed.

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

#1


24  

The answer is technically YES they're different, but practically NO they're not, unless you code your own accessors.

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

Let me explain. For an object pointer property, say @property NSObject *foo there is a clear and important difference in the code that gets generated if you use synthesize the accessors. This is described in the Apple documentation where it points out that the synthesized accessors will lock the object if the property is atomic (if you don't specify nonatomic, it becomes atomic by default)

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

So for object properties In a very crude nutshell: nonatomic is faster but not threadsafe, atomic (you can't specify it, but its the default) is threadsafe but potentially slower.

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

(Note: if you're accustomed to Java, you can think of using nonatomic as like not specifying synchronized, and not specifying nonatomic as like specifying synchronized. In other words atomic = synchronized)

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

But a BOOL is a primitive - in fact a C signed char, so access should be atomic without locking the object as mentioned in Jano's answer. So when you're synthesizing the accessor, there are two possibilities: 1: the compiler is smart and sees that the property is primitive and avoids locking it, and 2: the compiler always locks the object for atomic properties

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

As far as I can see this is not documented anywhere, so I tried it by using the Generate->Assembly option in XCode and comparing it. The answer was not completely conclusive, but close enough to say that I'm almost certain the answer is #1, the compiler is smart. I say this, because the assembly code generated for an atomic object property is considerably different (more of it) than for a non-atomic object property: this is all the code for locking the object. For a BOOL property on the other hand, there was only one line different - a single "mov" which doesn't look like it could possibly make a difference. Still I wonder. Interestingly, another difference is that the atomic version of BOOL has some extra commented-outlines for debugging - so the compiler is clearly treating it differently.

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

Nonetheless the similarity is such that I would say they're the same for practical purposes.

尽管如此,我认为它们的相似之处在于实际目的。

But they're still technically different and may be substantively different in some other library you're reading (that you didn't code yourself) if you can't see the implementation, and here's why: atomic properties have a contract. The contract says: "If you access my value on multiple threads, I promise that each setting or getting operation will complete before any other begins".

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

But, you say, BOOL is still naturally atomic, so isn't this contract implicit?

但是,你说,BOOL仍然是自然原子的,所以这个合约是否隐含?

No. A BOOL variable is naturally atomic, but we're talking about a property. A property might not be synthesized and might not even have a single variable to back it up. This is actually pretty common. Consider:

不。BOOL变量自然是原子的,但我们谈的是属性。属性可能无法合成,甚至可能没有单个变量来备份它。这实际上很常见。考虑:

@property (getter=isEmptyThingo) BOOL emptyThingo;

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

who knows what's going on in deriveTheThingo!? Okay, this is a bit contrived, but the point is that isEmptyThingo - our getter doesn't look very atomic, does it? What happens if one thread is deriving the thingo and another thread comes calling to find if its empty.

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

Long story short: the property is not atomic. So we should declare it so.

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

Hence m original answer qualified: if you're writing this property yourself and using @synthesize, then they're probably the same, but you should generally not treat them the same.

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

As a rule of thumb, if you don't need multithreaded support - which you generally don't if you're working in UI code like UIViewControllers, then just declare it all nonatomic.

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

#2


8  

In a x-bit architecture (eg: 32bit, 64bit, etc.) any value which is x or less bits will always be read or written atomically. This is a property of any sane hardware implementation.

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

The default atomic property means that a property value is always set or get in whole, disregarding what other threads are doing. This is only a concern for properties that exceed the number of bits of the architecture. Nonatomic is completely ignored by the compiler on any other type.

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

Example:

@property struct { int d; } flag;
@property (atomic) struct { float x; float y; } point;
@property (atomic,copy) NSString *s;
  • struct { int d; } is already atomic so the accesors don't need mutual exclusion.

    struct {int d;已经是原子的,所以加法器不需要互斥。

  • struct { float x, float y} could be in an inconsistent state if it wasn't atomic. Example: two threads setting {1,2} and {3,4} could overlap the writing, and the struct could end up with a value from each set: {1,4}.

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

  • Pointers are stored in a single memory position but they require several statements for memory management.
  • 指针存储在单个内存位置,但它们需要多个语句来进行内存管理。

Atomic properties contribute to thread safety avoiding race conditions that lead to inconsistent values or memory management mishaps. This alone doesn't guarantee thread safety because it doesn't deal with other problems like deadlock, starvation, visibility, etc.

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

#3


2  

Yes. nonatomic is not a memory-management keyword, it has to do with thread safety. Also, properties are atomic by default (without explicitly declaring them as nonatomic), so there is a difference between the two declarations you listed.

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