问题描述
在Objective-C中将原始类型的属性指定为nonatomic
是否有意义?
我想知道这两个属性之间的区别:
@property (nonatomic) BOOL myBool;
@property BOOL myBool;
从技术上讲,答案是肯定的,它们是不同的,但是实际上,答案不是肯定的,除非您编写自己的访问器.
让我解释一下.对于对象指针属性,说@property NSObject *foo
,如果您使用合成访问器,则生成的代码有明显而重要的区别. Apple文档中对此进行了说明如果属性是原子的,则合成的访问器将锁定对象(如果您未指定非原子的,则默认情况下它将变为原子的)
因此,对于对象属性而言,简而言之:非原子的速度更快,但不是线程安全的;原子的(您不能指定它,但它是默认值)是线程安全的,但可能会更慢.
(注意:如果您习惯使用Java,可以考虑使用nonatomic
就像 not 指定synchronized
,而 not 指定nonatomic
就像指定synchronized
.换句话说,atomic =同步)
但是BOOL是原始的-实际上是C签名的char,因此访问应该是原子的,而不必像Jano的答案中所述锁定对象.因此,当您合成访问器时,有两种可能性:1:编译器很聪明,并且看到该属性是原始属性,因此避免了对其进行锁定,并且2:编译器始终为原子属性锁定对象
据我所知,这在任何地方都没有记录,因此我尝试使用XCode中的Generate-> Assembly选项并对其进行比较.答案不是完全结论性的,但是可以说我几乎可以肯定答案是#1,编译器很聪明.我之所以这样说,是因为为原子对象属性生成的汇编代码与为非原子对象属性生成的汇编代码有很大不同(更多):这是所有用于锁定对象的代码.另一方面,对于BOOL属性,只有一行不同-看起来不像它的单个"mov"可能会有所作为.我还是很奇怪.有趣的是,另一个区别是BOOL的原子版本还有一些额外的注释概述可用于调试-因此,编译器显然会以不同的方式对待它.
尽管如此,我还是会说它们在实际用途上是相同的.
但是,如果您看不到实现,它们在技术上仍然有所不同,并且在您正在阅读的其他某些库中可能有实质性的不同(您自己没有编写代码),这就是为什么: atomic 属性具有合约.合同说:如果您在多个线程上访问我的值,则我保证每个设置或获取操作都将在其他任何操作开始之前完成".
但是,您说BOOL仍然是自然原子的,所以该合同不是隐含的吗?
不. BOOL 变量自然是原子的,但是我们正在谈论属性. 属性可能无法合成,甚至可能没有单个变量来备份.这实际上很常见.考虑:
@property (getter=isEmptyThingo) BOOL emptyThingo;
...
- (BOOL)isEmptyThingo
{
Thingo *thingo = [self deriveTheThingo];
if ([thingo length] == 0) {
return YES;
}
return NO;
}
谁知道deriveTheThingo
中发生了什么!好的,这有点做作,但是重点是isEmptyThingo-我们的getter看起来不是很原子,不是吗?如果一个线程派生了东西,而另一个线程调用来查找它是否为空,将会发生什么.
因此,原始答案合格:如果您自己编写此属性并使用@synthesize,则它们可能是相同的,但通常不应将它们相同.
根据经验,如果您不需要多线程支持-如果您使用的是UIViewControllers这样的UI代码,则通常不需要多线程支持,只需声明所有这些都是非原子的即可.
In Objective-C Does it ever make sense to specify a property for a primitive type as nonatomic
?
I'm wondering about the difference between these two properties:
@property (nonatomic) BOOL myBool;
@property BOOL myBool;
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)
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)
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, and2: the compiler always locks the object for atomic properties
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.
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?
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:
@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.
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.
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.
这篇关于基本类型的Objective-C属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!