做什么 atomic
和 nonatomic
在财产声明中意味着什么?
@property(nonatomic, retain) UITextField *userName;
@property(atomic, retain) UITextField *userName;
@property(retain) UITextField *userName;
这三者之间的运作区别是什么?
做什么 atomic
和 nonatomic
在财产声明中意味着什么?
@property(nonatomic, retain) UITextField *userName;
@property(atomic, retain) UITextField *userName;
@property(retain) UITextField *userName;
这三者之间的运作区别是什么?
最后两个是相同的; “atomic”是默认行为(请注意,它实际上不是关键字;它仅由缺少指定 - nonatomic
atomic
在最新版本的llvm / clang中添加了关键字。
假设你是@synthesizing方法实现,原子与非原子会改变生成的代码。如果您正在编写自己的setter / getter,则atomic / nonatomic / retain / assign / copy仅仅是建议性的。 (注意:@synthesize现在是最近版本的LLVM中的默认行为。也没有必要声明实例变量;它们也将自动合成,并且将具有 _
以他们的名字为前缀以防止意外直接访问)。
使用“原子”,合成的setter / getter将确保a 整个 无论任何其他线程上的setter活动如何,值始终从getter返回或由setter设置。也就是说,如果线程A位于getter的中间,而线程B调用setter,则实际可行的值 - 一个自动释放的对象,很可能 - 将返回给A中的调用者。
在 nonatomic
,没有这样的保证。从而, nonatomic
比“原子”快得多。
什么是“原子” 不 对线程安全做出任何保证。如果线程A与线程B同时调用getter并且C调用具有不同值的setter,则线程A可以获得返回的三个值中的任何一个 - 在调用任何setter之前的值或者传递给setter的任一值在B和C中。同样,对象可能最终得到B或C的值,无法分辨。
确保数据完整性 - 多线程编程的主要挑战之一 - 是通过其他方式实现的。
添加到此:
atomicity
当多个依赖属性在起作用时,单个属性也不能保证线程安全。
考虑:
@property(atomic, copy) NSString *firstName;
@property(atomic, copy) NSString *lastName;
@property(readonly, atomic, copy) NSString *fullName;
在这种情况下,线程A可以通过调用重命名对象 setFirstName:
然后打电话 setLastName:
。同时,线程B可以调用 fullName
在线程A的两个调用之间,将接收新的名字和旧的姓氏。
要解决这个问题,你需要一个 交易模型。即一些其他类型的同步和/或排除,允许一个人排除访问权限 fullName
而依赖属性正在更新。
这在Apple的解释中有所解释 文件,但下面是一些实际发生的例子。请注意,没有“atomic”关键字,如果未指定“nonatomic”,则属性为atomic,但明确指定“atomic”将导致错误。
//@property(nonatomic, retain) UITextField *userName;
//Generates roughly
- (UITextField *) userName {
return userName;
}
- (void) setUserName:(UITextField *)userName_ {
[userName_ retain];
[userName release];
userName = userName_;
}
现在,原子变体有点复杂:
//@property(retain) UITextField *userName;
//Generates roughly
- (UITextField *) userName {
UITextField *retval = nil;
@synchronized(self) {
retval = [[userName retain] autorelease];
}
return retval;
}
- (void) setUserName:(UITextField *)userName_ {
@synchronized(self) {
[userName_ retain];
[userName release];
userName = userName_;
}
}
基本上,原子版本必须采取锁定以保证线程安全,并且还会碰撞对象的引用计数(以及自动释放计数以平衡它),以便保证对象存在对象,否则存在如果另一个线程正在设置该值,则是一个潜在的竞争条件,导致引用计数降至0。
根据属性是标量值还是对象,以及如何保留,复制,只读,非原子等交互,实际上有很多不同的变体如何工作。一般来说,属性合成器只知道如何为所有组合做“正确的事”。
理解差异的最佳方法是使用以下示例。
假设有一个名为“name”的原子字符串属性,如果你打电话 [self setName:@"A"]
从线程A,调用 [self setName:@"B"]
从线程B,并打电话 [self name]
从线程C开始,不同线程上的所有操作都将按顺序执行,这意味着如果一个线程正在执行setter或getter,那么其他线程将等待。
这使得属性“name”读/写安全,但如果另一个线程D调用 [name release]
同时这个操作可能会导致崩溃,因为这里没有涉及setter / getter调用。这意味着对象是读/写安全的(ATOMIC),但不是线程安全的,因为另一个线程可以同时向对象发送任何类型的消息。开发人员应确保此类对象的线程安全性。
如果属性“name”是非原子的,那么上面例子中的所有线程--A,B,C和D将同时执行,产生任何不可预测的结果。在原子的情况下,A,B或C中的任何一个将首先执行,但D仍然可以并行执行。
这个问题的其他优秀答案已经很好地定义了语法和语义。因为 执行 和 性能 不详细,我会补充我的答案。
这3个有什么功能差异?
我一直认为原子作为一个非常好奇的默认。在我们工作的抽象级别,使用类的原子属性作为实现100%线程安全的工具是一个极端情况。对于真正正确的多线程程序,程序员的干预几乎肯定是必需的。同时,性能特征和执行还没有详细说明。多年来编写了一些大量多线程的程序后,我一直在声明我的属性 nonatomic
整个时间因为原子对任何目的都不合理。在讨论原子和非原子属性的细节时 这个问题,我做了一些分析,遇到了一些好奇的结果。
执行
好。我想要澄清的第一件事是锁定实现是实现定义和抽象的。路易斯使用 @synchronized(self)
在他的例子中 - 我已经看到这是混乱的常见根源。实施没有 其实 使用 @synchronized(self)
;它使用对象级别 旋锁。路易斯的插图很适合使用我们都熟悉的结构的高级插图,但重要的是要知道它不使用 @synchronized(self)
。
另一个区别是原子属性将保留/释放你的对象在getter中。
性能
这是有趣的部分:使用原子属性访问的性能 无可争议 (例如,单线程)案例在某些情况下可能非常快。在不太理想的情况下,使用原子访问可能花费超过20倍的开销 nonatomic
。虽然 争议 使用7个线程的情况对于三字节结构(2.2 GHz)来说慢了44倍 酷睿i7 四核,x86_64)。三字节结构是一个非常慢的属性的例子。
有趣的注意事项:三字节结构的用户定义访问器比合成的原子访问器快52倍;或84%的合成非原子访问器的速度。
有争议案件中的物品也可超过50次。
由于实施中的优化和变化的数量,在这些环境中测量实际影响是非常困难的。您可能经常听到类似“信任它,除非您分析并发现它是一个问题”的内容。由于抽象级别,实际上很难衡量实际影响。从配置文件收集实际成本可能非常耗时,并且由于抽象,非常不准确。同样,ARC vs MRC可以产生很大的不同。
让我们退后一步, 不 关注财产访问的实施,我们将包括通常的嫌疑人 objc_msgSend
,并检查一些真实世界的高级别结果,许多调用a NSString
得到的 无可争议 案例(以秒为单位的值):
正如您可能已经猜到的那样,引用计数活动/循环是原子学和ARC下的重要贡献者。您还会看到有争议的案例存在更大的差异。
虽然我非常关注表现,但我仍然说 语义第一!。同时,对许多项目来说,性能是一个低优先级。但是,了解执行细节和您使用的技术成本肯定不会受到影响。您应该根据自己的需要,目的和能力使用正确的技术。希望这将为您节省几个小时的比较,并帮助您在设计程序时做出更明智的决策。
原子=线程安全
非原子 =没有线程安全
如果实例变量在从多个线程访问时行为正确,则它们是线程安全的,无论运行时环境是否调度或交错执行这些线程,并且调用代码没有额外的同步或其他协调。
如果线程更改了实例的值,则更改的值可用于所有线程,并且只有一个线程可以一次更改该值。
atomic
:如果要在多线程环境中访问实例变量。
atomic
:没有那么快 nonatomic
因为 nonatomic
从运行时开始不需要任何监视器工作。
nonatomic
:如果实例变量不会被多个线程更改,则可以使用它。它提高了性能。
我发现原子和非原子属性的解释非常好 这里。以下是相同的相关文字:
'原子'意味着它不能被分解。 在OS /编程术语中,原子函数调用是一个不能被中断的函数 - 整个函数必须执行,并且不会被操作系统通常的上下文切换换出CPU直到它完成。以防您不知道:由于CPU一次只能执行一项操作,因此操作系统会在很短的时间内将CPU的访问权限转移到所有正在运行的进程,以便 错觉 多任务处理。 CPU调度程序可以(并且确实)在执行过程中的任何时刻中断进程 - 即使在函数调用期间也是如此。因此,对于更新共享计数器变量的操作,其中两个进程可以尝试同时更新变量,它们必须“原子地”执行,即每个更新操作必须完全完成才能将任何其他进程交换到中央处理器。
所以我猜测在这种情况下原子意味着属性读取器方法不能被中断 - 实际上意味着方法读取的变量不能在中途改变它们的值,因为其他一些线程/调用/函数得到了交换到CPU上。
因为 atomic
变量不能被中断,它们在任何一点所包含的值都是(线程锁定)保证的 清廉但是,确保此线程锁定可以更慢地访问它们。 non-atomic
另一方面,变量没有这样的保证,但确实提供了更快捷的访问。总结一下,去吧 non-atomic
当你知道多个线程不会同时访问你的变量并加快速度时。
在阅读了这么多文章后,Stack Overflow发布并制作了演示应用程序以检查变量属性属性,我决定将所有属性信息放在一起:
atomic
//默认nonatomic
strong = retain
//默认weak = unsafe_unretained
retain
assign
//默认unsafe_unretained
copy
readonly
readwrite
//默认在文章中 iOS中的变量属性属性或修饰符 你可以找到所有上述属性,这肯定会对你有所帮助。
atomic
atomic
表示只有一个线程访问变量(静态类型)。atomic
是线程安全的。atomic
是默认行为例:
@property (retain) NSString *name;
@synthesize name;
nonatomic
nonatomic
表示多线程访问变量(动态类型)。nonatomic
线程不安全。nonatomic
不是默认行为。我们需要添加 nonatomic
属性属性中的关键字。例:
@property (nonatomic, retain) NSString *name;
@synthesize name;