iOS atomic 与 nonatomic
摘抄苹果官方文档为
Atomic is the default: if you don’t type anything, your property is atomic. An atomic property is guaranteed that if you try to read from it, you will get back a valid value. It does not make any guarantees about what that value might be, but you will get back good data, not just junk memory. What this allows you to do is if you have multiple threads or multiple processes pointing at a single variable, one thread can read and another thread can write. If they hit at the same time, the reader thread is guaranteed to get one of the two values: either before the change or after the change. What atomic does not give you is any sort of guarantee about which of those values you might get. Atomic is really commonly confused with being thread-safe, and that is not correct. You need to guarantee your thread safety other ways. However, atomic will guarantee that if you try to read, you get back some kind of value.
nonatomic
On the flip side, non-atomic, as you can probably guess, just means, “don’t do that atomic stuff.” What you lose is that guarantee that you always get back something. If you try to read in the middle of a write, you could get back garbage data. But, on the other hand, you go a little bit faster. Because atomic properties have to do some magic to guarantee that you will get back a value, they are a bit slower. If it is a property that you are accessing a lot, you may want to drop down to nonatomic to make sure that you are not incurring that speed penalty.
设置成员变量的@property属性时,默认为atomic,提供多线程安全。
在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。加了atomic,setter函数会变成下面这样:
{lock}
if (property != newValue) {
[property release];
property = [newValue retain];
}
{unlock}
也就说使用了atomic属性内部会通过加锁的机制来确保其get/set操作的准确性,防止在写为完成的时候被另一个线程读取,造成数据错误,缺点也是明显的在ios中加锁是一个很消耗系统资源的操作。
所以我们使用的属性大部分为nonatomic,原因就是使用atomic开销比较大,带来性能问题,而且使用atomic也不能保证对象多线程的安全,它只是只能保证你访问的时候给你返回一个完好无损的值而已。
例如:假设有一个 atomic 的属性 "name",如果线程 A 调[self setName:@"A"],线程 B 调[self setName:@"B"],线程 C 调[self name],那么所有这些不同线程上的操作都将依次顺序执行——也就是说,如果一个线程正在执行 getter/setter,其他线程就得等待。因此,属性 name 是读/写安全的。
但是,如果有另一个线程 D 同时在调[name release],那可能就会crash,因为 release 不受 getter/setter 操作的限制。也就是说,这个属性只能说是读/写安全的,但并不是线程安全的,因为别的线程还能进行读写之外的其他操作。线程安全需要开发者自己来保证。
如果 name 属性是 nonatomic 的,那么上面例子里的所有线程 A、B、C、D 都可以同时执行,可能导致无法预料的结果。如果是 atomic 的,那么 A、B、C 会串行,而 D 还是并行的。
我们可以认为atomic只对一个属性的getter/setter是安全的,其它的线程安全是无法保证的。
现在总结一下nonatomic与atomic的区别:
atomic:
系统默认
会保证 CPU 能在别的线程来访问这个属性之前,先执行完当前流程
性能低,速度慢
atomic 对象的getter/setter方法实现:
-
(void)setCurrentImage:(UIImage *)currentImage
{
@synchronized(self) {
if (_currentImage != currentImage) {
[_currentImage release];
_currentImage = [currentImage retain];}
}
} -
(UIImage *)currentImage
{
@synchronized(self) {
return _currentImage;
}
}
nonatomic:
不是默认的
速度快
线程不安全的
nonatomic 对象的getter/setter方法实现: -
(void)setCurrentImage:(UIImage *)currentImage
{
if (_currentImage != currentImage) {
[_currentImage release];
_currentImage = [currentImage retain];}
} -
(UIImage *)currentImage
{
return _currentImage;
}