原子属性和非原子属性之间有什么区别?

What's the difference between the atomic and nonatomic attributes?

提问人:Alex Wayne 提问时间:2/26/2009 最后编辑:Chirag KothiyaAlex Wayne 更新时间:5/3/2023 访问量:530778

问:

财产声明中的含义和含义是什么?atomicnonatomic

@property(nonatomic, retain) UITextField *userName;
@property(atomic, retain) UITextField *userName;
@property(retain) UITextField *userName;

这三者之间的操作区别是什么?

IOS Objective-C 属性 Atomic Nonatomic

答:

63赞 Andrew Grant 2/26/2009 #1

原子:

原子保证对属性的访问将以原子方式执行。例如,它总是返回一个完全初始化的对象,一个线程上任何属性的 get/set 都必须完成,然后另一个线程才能访问它。

如果你想象以下函数同时发生在两个线程上,你就会明白为什么结果会不漂亮。

-(void) setName:(NSString*)string
{
  if (name)
  {
    [name release]; 
    // what happens if the second thread jumps in now !?
    // name may be deleted, but our 'name' variable is still set!
    name = nil;
  }

  ...
}

优点:每次返回完全初始化的对象使其成为多线程的最佳选择。

缺点:性能下降,使执行速度变慢

非原子 :

与 Atomic 不同,它不能确保每次都返回完全初始化的对象。

优点:极快的执行速度。

缺点:在多线程的情况下出现垃圾值的可能性。

评论

5赞 Andrew Grant 2/26/2009
这个评论没有多大意义。你能澄清一下吗?如果您查看 Apple 网站上的示例,则 atomic 关键字会在更新其属性时在对象上同步。
53赞 Jay O'Conor 2/26/2009 #2

最简单的答案是:你的后两个例子之间没有区别。默认情况下,属性访问器是原子的。

非垃圾回收环境中的原子访问器(即使用 retain/release/autorelease 时)将使用锁来确保另一个线程不会干扰值的正确设置/获取。

请参阅 Apple 的 Objective-C 2.0 文档的“性能和线程”部分,了解更多信息以及创建多线程应用时的其他注意事项。

评论

8赞 Louis Gerbarg 2/26/2009
有两个原因。首先,对于合成代码,它生成速度更快(但不是线程安全代码)。其次,如果您编写的客户访问器不是原子的,则可以在将来的任何用户读取其接口时注释代码不是原子的,而无需实现它们。
366赞 Louis Gerbarg 2/26/2009 #3

这在 Apple 的文档中进行了解释,但以下是实际发生的一些示例。

请注意,没有“atomic”关键字,如果不指定“nonatomic”,则该属性为atomic,但显式指定“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_;
    }
}

基本上,原子版本必须采取锁来保证线程安全,并且还增加了对象上的引用计数(以及自动释放计数以平衡它),以便保证对象对调用者存在,否则如果另一个线程正在设置该值,则存在潜在的竞争条件, 导致 ref 计数降至 0。

实际上,这些东西的工作方式有很多不同的变体,具体取决于属性是标量值还是对象,以及保留、复制、只读、非原子等如何交互。一般来说,属性合成器只知道如何为所有组合做“正确的事情”。

评论

8赞 Florin 8/12/2010
@Louis Gerbarg:我相信,如果您尝试分配相同的对象(即:userName == userName_),您的(非原子,保留)setter 版本将无法正常工作
5赞 tc. 12/2/2010
您的代码略有误导性;无法保证哪些原子 getter/setter 是同步的。至关重要的是,在任何东西上都不同步(iOS SDK GCC 4.2 ARM ),这意味着 和 之间存在竞争。请参阅 stackoverflow.com/questions/917884/...@property (assign) id delegate;-Os[self.delegate delegateMethod:self];foo.delegate = nil; self.foo = nil; [super dealloc];
0赞 tc. 12/10/2013
@fyolnish我不确定/是什么,但不,不是真的。原子 / 属性的 getter 需要确保它不会返回一个对象,该对象的 refcount 由于在另一个线程中被调用而变为零,这本质上意味着它需要读取 ivar,保留它,同时确保 setter 没有覆盖和释放它,然后自动释放它以平衡保留。这实质上意味着 getter 和 setter 都必须使用锁(如果内存布局是固定的,它应该可以用 CAS2 指令实现;唉,这是一个方法调用)。_valvalcopyretain-retain
0赞 Fjölnir 12/11/2013
@tc 已经有一段时间了,但我想写的可能是这样的: gist.github.com/fjolnir/5d96b3272c6255f6baae 但是,是的,旧值有可能在 setFoo: 返回之前被读取,并在读取器返回之前释放。但是,也许如果 setter 使用 -autorelease 而不是 -release,这将解决这个问题。
0赞 tc. 12/11/2013
@fyolnish 不幸的是,不可以:它在 setter 的线程上自动释放,而它需要在 getter 的线程上自动释放。看起来也有(很小的)机会用完堆栈,因为你使用的是递归。
1807赞 bbum 2/26/2009 #4

最后两个是相同的;“atomic”是默认行为(请注意,它实际上不是一个关键字;它只是通过缺少非原子来指定 -- 在最新版本的 llvm/clang 中被添加为关键字)。atomic

假设您@synthesizing方法实现,原子与非原子会更改生成的代码。如果你正在编写自己的 setter/getter,atomic/nonatomic/retain/assign/copy 只是建议。(注意:@synthesize现在是最新版本的 LLVM 中的默认行为。也不需要声明实例变量;它们也将自动合成,并在其名称前面加上一个以防止意外直接访问)。_

使用“原子”,合成的 setter/getter 将确保始终从 getter 返回由 setter 设置整个值,而不管任何其他线程上的 setter 活动如何。也就是说,如果线程 A 位于 getter 的中间,而线程 B 调用 setter,则实际的可行值(很可能是自动释放的对象)将返回给 A 中的调用者。

在 中,不作任何此类保证。因此,比“原子”快得多。nonatomicnonatomic

“原子”做的是对线程安全做出任何保证。如果线程 A 同时调用 getter,线程 B 和 C 调用具有不同值的 setter,则线程 A 可能会返回三个值中的任何一个 -- 调用任何 setter 之前的值,或者传递给 B 和 C 中的 setter 的值中的任何一个。 没办法说。

确保数据完整性 - 多线程编程的主要挑战之一 - 可以通过其他方式实现。

除此之外:

atomicity当多个依赖属性起作用时,单个属性也无法保证线程安全。

考虑:

 @property(atomic, copy) NSString *firstName;
 @property(atomic, copy) NSString *lastName;
 @property(readonly, atomic, copy) NSString *fullName;

在这种情况下,线程 A 可以通过调用然后调用 来重命名对象。同时,线程 B 可能会在线程 A 的两次调用之间调用,并将接收新的名字和旧姓氏。setFirstName:setLastName:fullName

为了解决这个问题,你需要一个事务模型。即某种其他类型的同步和/或排除,允许在更新依赖属性时排除对的访问。fullName

评论

22赞 Daniel Dickison 5/25/2011
鉴于任何线程安全代码都会执行自己的锁定等,您什么时候要使用原子属性访问器?我想不出一个好的例子。
8赞 Ben Flynn 1/27/2012
@bbum 有道理。我喜欢你对另一个答案的评论,即线程安全更像是一个模型级别的问题。根据 IBM 线程安全定义: ibm.co/yTEbjY “如果一个类被正确实现,这是它符合其规范的另一种说法,那么对该类的对象的任何操作序列(读取或写入公共字段以及调用公共方法)都不应能够将该对象置于无效状态, 观察对象处于无效状态,或违反类的任何不变量、前提条件或后条件。
6赞 bugloaf 2/15/2013
下面是一个类似于 @StevenKramer 的示例:我有一个列出要在 UI 中显示的数据。当应用程序启动时,指针指向一个空数组,然后应用从 Web 拉取数据。当 Web 请求完成(在不同的线程中)时,应用会生成一个新数组,然后以原子方式将该属性设置为新的指针值。它是线程安全的,我不必编写任何锁定代码,除非我遗漏了什么。对我来说似乎很有用。@property NSArray* astronomicalEvents;
13赞 bbum 11/24/2013
@HotLicks 另一个有趣的;在某些架构上(不记得是哪一种),作为参数传递的 64 位值可能一半在寄存器中传递,一半在堆栈中传递。 防止跨线程半值读取。(这是一个有趣的错误。atomic
8赞 bbum 12/6/2013
@congliu线程 A 返回一个没有跳舞的对象。线程 B 释放对象。线程 A 开始繁荣。 确保线程 A 对返回值具有强引用(+1 保留计数)。retain/autoreleaseatomic
12赞 4 revs, 3 users 67%Deepak #5

没有这样的关键词“原子”

@property(atomic, retain) UITextField *userName;

我们可以使用上面的

@property(retain) UITextField *userName;

请参阅堆栈溢出问题,如果我使用 @property(atomic,retain)NSString *myString,我会遇到问题

评论

11赞 Matthijn 2/25/2013
“有这样的关键字”,该关键字默认不需要,即使是默认值也不意味着该关键字不存在。
5赞 sethfri 5/20/2015
这是不正确的。关键字确实存在。这个答案具有误导性,我鼓励将其删除。
143赞 Vijayendra 2/1/2012 #6

了解差异的最佳方法是使用以下示例。

假设有一个名为“name”的原子字符串属性,如果你从线程 A 调用,从线程 B 调用,从线程 C 调用,那么不同线程上的所有操作都将串行执行,这意味着如果一个线程正在执行 setter 或 getter,那么其他线程将等待。[self setName:@"A"][self setName:@"B"][self name]

这使得属性“name”读/写安全,但如果另一个线程 D 同时调用,则此操作可能会产生崩溃,因为此处不涉及 setter/getter 调用。这意味着一个对象是读/写安全的 (ATOMIC),但不是线程安全的,因为另一个线程可以同时向该对象发送任何类型的消息。开发人员应确保此类对象的线程安全。[name release]

如果属性“name”是非原子的,那么上面示例中的所有线程 - A、B、C 和 D 将同时执行,产生任何不可预测的结果。在原子的情况下,A、B 或 C 中的任何一个将首先执行,但 D 仍然可以并行执行。

70赞 tipycalFlow 2/24/2012 #7

在这里找到了一个关于原子和非原子性质的很好的解释。以下是一些来自同一内容的相关文本:

“原子”意味着它不能被分解。 在操作系统/编程术语中,原子函数调用是一个不能中断的函数 - 整个函数必须执行,并且在完成之前不会通过操作系统通常的上下文切换从 CPU 中交换出来。以防万一你不知道:由于 CPU 一次只能做一件事,操作系统会在很小的时间片内将对 CPU 的访问轮换到所有正在运行的进程,以给人一种多任务处理的错觉。CPU 调度程序可以(并且确实)在进程执行的任何时候中断进程 - 即使在函数调用中间也是如此。因此,对于像更新共享计数器变量这样的操作,其中两个进程可以尝试同时更新变量,它们必须以“原子方式”执行,即每个更新操作必须完整完成,然后才能将任何其他进程交换到 CPU 上。

所以我猜在这种情况下,原子意味着属性读取器方法不能中断 - 实际上意味着该方法读取的变量不能在中途更改其值,因为其他一些线程/调用/函数被交换到 CPU 上。

由于变量不能被中断,因此它们在任何时候包含的值(线程锁)都保证不会损坏,尽管确保此线程锁会使访问它们的速度变慢。 另一方面,变量不能提供这样的保证,但确实提供了更快访问的奢侈。总而言之,当你知道你的变量不会被多个线程同时访问时,就去加速。atomicnon-atomicnon-atomic

评论

1赞 Rob 3/11/2019
链接已断开。;(
1赞 tipycalFlow 3/9/2020
这就是链接的问题:(幸运的是,我在回答中引用了相关文本
177赞 raw3d 5/25/2012 #8

原子

  • 是默认行为
  • 将确保在另一个进程访问变量之前,CPU 完成当前进程
  • 速度不快,因为它确保了该过程完全完成

非原子

  • 不是默认行为
  • 更快(对于合成代码,即对于使用 @property 和 @synthesize 创建的变量)
  • 不是线程安全的
  • 当两个不同的进程同时访问同一变量时,可能会导致意外行为
120赞 justin 8/18/2012 #9

语法和语义已经由这个问题的其他优秀答案很好地定义了。因为执行性能不是很详细,所以我会补充我的答案。

这三者在功能上有什么区别?

我一直认为原子是默认的,这很奇怪。在我们工作的抽象级别上,使用类的原子属性作为载体来实现 100% 的线程安全是一个极端情况。对于真正正确的多线程程序,程序员的干预几乎肯定是必需的。同时,性能特征和执行尚未深入详细说明。这些年来,我编写了一些高度多线程的程序,我一直在声明我的属性,因为原子对于任何目的都是不明智的。在讨论原子和非原子性质的细节这个问题时,我做了一些分析,遇到了一些奇怪的结果。nonatomic

执行

还行。我想澄清的第一件事是,锁定实现是实现定义和抽象的。路易斯在他的例子中引用了 -- 我把这看作是造成混淆的常见根源。实现实际上并不使用 ;它使用对象级旋转锁。Louis 的插图非常适合使用我们都熟悉的结构的高级插图,但重要的是要知道它不使用 .@synchronized(self)@synchronized(self)@synchronized(self)

另一个区别是原子属性将在 getter 中保留/释放对象循环。

性能

有趣的是:在某些情况下,在无争议(例如单线程)的情况下,使用原子属性访问的性能可能非常快。在不太理想的情况下,使用原子访问的成本可能是 的 20 倍以上。而使用 7 个线程的有争议的案例对于三字节结构(2.2 GHz Core i7 Quad Core,x86_64)慢 44 倍。三字节结构是一个非常慢的属性的示例。nonatomic

有趣的旁注:三字节结构的用户定义访问器比合成的原子访问器快 52 倍;或合成非原子接入器速度的 84%。

有争议的案件中的对象也可以超过 50 次。

由于优化的数量和实现的变化,在这些情况下衡量现实世界的影响是相当困难的。您可能经常听到类似“相信它,除非您分析并发现它是一个问题”。由于抽象级别,实际上很难衡量实际影响。从配置文件中收集实际成本可能非常耗时,并且由于抽象,非常不准确。同样,ARC 与 MRC 可以产生很大的不同。

因此,让我们退后一步,关注属性访问的实现,我们将包括常见的可疑因素,例如 ,并检查一些真实世界的高级结果,这些结果在无争议的情况下对 getter 进行了多次调用(以秒为单位的值):objc_msgSendNSString

  • 湄公河委员会 |非原子 |手动实现的 getter:2
  • 湄公河委员会 |非原子 |合成吸气剂:7
  • 湄公河委员会 |原子 |合成吸气剂:47
  • 电弧 |非原子 |合成的 getter:38(注意:ARC 在此处添加 ref count 循环)
  • 电弧 |原子 |合成吸气剂:47

正如您可能已经猜到的那样,参考计数活动/循环是原子和 ARC 下的重要贡献者。在有争议的案件中,您还会看到更大的差异。

虽然我非常关注性能,但我仍然说语义第一!同时,对于许多项目来说,性能的优先级较低。但是,了解您使用的技术的执行细节和成本肯定不会有什么坏处。您应该根据自己的需求、目的和能力使用正确的技术。希望这将为您节省几个小时的比较时间,并帮助您在设计程序时做出更明智的决定。

评论

0赞 SDEZero 8/27/2012
湄公河委员会 |原子 |合成吸气剂:47 ARC |原子 |合成的吸气剂:47 是什么让它们相同?ARC 不应该有更多的开销吗?
2赞 Kunal Balani 9/18/2013
因此,如果原子属性不好,它们是否默认。要增加样板代码?
0赞 justin 9/23/2013
@LearnCocos2D我刚刚在同一台机器上测试了 10.8.5,目标是 10.8,对于具有非不朽的单线程无争议情况: -- 今天的结果略有不同。我没有做任何比较。 在语义上是不同的,如果你有重要的并发程序,我认为它不是一个好的工具。如果您需要速度,请避免 .NSString-ARC atomic (BASELINE): 100% -ARC nonatomic, synthesised: 94% -ARC nonatomic, user defined: 86% -MRC nonatomic, user defined: 5% -MRC nonatomic, synthesised: 19% -MRC atomic: 102%@synchronized@synchronized@synchronized
0赞 CodeSmile 9/23/2013
你在某个地方有这个测试吗?我在这里不断添加我的:github.com/LearnCocos2D/LearnCocos2D/tree/master/......
0赞 Bradley Thomas 2/24/2016
我总是觉得有趣的是,人们会争论是要把东西做得快得令人难以置信,还是快 50 倍,但两者之间没有明显的区别。这就像拥有一个视网膜显示器和另一个分辨率为 50 倍的显示器。如果对任何人都没有影响,为什么要浪费资源来获得这种性能水平?特别是当健壮的代码可以节省数天的调试时间时......
31赞 IOS Rocks 11/22/2012 #10

原子意味着只有一个线程访问变量(静态类型)。Atomic是线程安全的,但它很慢。

非原子意味着多个线程访问变量(动态类型)。非原子是线程不安全的,但它速度很快。

79赞 swiftBoy 3/21/2013 #11

在阅读了这么多文章、Stack Overflow 帖子并制作了演示应用程序来检查变量属性属性之后,我决定将所有属性信息放在一起:

  1. atomic违约
  2. nonatomic
  3. strong = retain违约
  4. weak = unsafe_unretained
  5. retain
  6. assign违约
  7. unsafe_unretained
  8. copy
  9. readonly
  10. readwrite违约

iOS 中的变量属性属性或修饰符一文中,您可以找到上述所有属性,这肯定会对您有所帮助。

  1. atomic

    • atomic表示只有一个线程访问变量(静态类型)。
    • atomic是线程安全的。
    • 但它的性能很慢
    • atomic是默认行为
    • 非垃圾回收环境中的原子访问器(即使用 retain/release/autorelease 时)将使用锁来确保另一个线程不会干扰值的正确设置/获取。
    • 它实际上不是一个关键字。

    例:

        @property (retain) NSString *name;
    
        @synthesize name;
    
  2. nonatomic

    • nonatomic表示多线程访问变量(动态类型)。
    • nonatomic线程不安全。
    • 但它的性能很快
    • nonatomic不是默认行为。我们需要在 property 属性中添加关键字。nonatomic
    • 当两个不同的进程(线程)同时访问同一变量时,可能会导致意外行为。

    例:

        @property (nonatomic, retain) NSString *name;
    
        @synthesize name;
    

评论

0赞 BangOperator 7/30/2016
如何分配和强/保留都是默认的?
0赞 abdullahselek 4/5/2017
strong 随 ARC 一起提供,retain 在 ARC 之前是默认的
97赞 Durai Amuthan.H 7/10/2013 #12

原子 = 线程安全

非原子 = 无线程安全

螺纹安全:

如果实例变量在从多个线程访问时行为正确,则实例变量是线程安全的,而不管运行时环境如何调度或交错执行这些线程,并且调用代码没有额外的同步或其他协调。

在我们的语境中:

如果线程更改了实例的值,则更改后的值可供所有线程使用,并且一次只能有一个线程更改该值。

使用地点 :atomic

如果要在多线程环境中访问实例变量。

含义:atomic

没有那么快,因为不需要从运行时进行任何看门狗工作。nonatomicnonatomic

使用地点 :nonatomic

如果实例变量不会被多个线程更改,则可以使用它。它提高了性能。

评论

5赞 Fattie 6/1/2014
你在这里说的一切都是正确的,但最后一句话本质上是“错误的”,Dura,对于今天的编程。你真的难以想象你会费心去尝试以这种方式“提高性能”。(我的意思是,在你进入光年之前,你会“不使用 ARC”、“不使用 NSString,因为它很慢!”等等。举个极端的例子,这就像说“团队,不要在代码中添加任何注释,因为这会减慢我们的速度。没有现实的开发管道,你会希望(不存在的)理论性能提升,而不可靠。
3赞 Durai Amuthan.H 6/1/2014
@JoeBlow这是事实,您可以在这里验证它 developer.apple.com/library/mac/documentation/Cocoa/Conceptual/......
3赞 Rob 3/10/2020
Durai,FWIW,这个链接直接与你的“原子=线程安全”的论点相矛盾。在文档中,苹果明确指出,“属性原子性并不等同于对象的线程安全。在实践中,原子很少足以实现线程安全。
11赞 Binarian 9/27/2013 #13

默认值为 ,这意味着每当使用该属性时,它确实会降低性能,但它是线程安全的。Objective-C 所做的是设置一个锁,因此只要执行 setter/getter 就只有实际的线程才能访问该变量。atomic

具有 ivar _internal的属性的 MRC 示例:

[_internal lock]; //lock
id result = [[value retain] autorelease];
[_internal unlock];
return result;

所以最后两个是一样的:

@property(atomic, retain) UITextField *userName;

@property(retain) UITextField *userName; // defaults to atomic

另一方面,不会向代码添加任何内容。因此,只有您自己编写安全机制代码时,它才是线程安全的。nonatomic

@property(nonatomic, retain) UITextField *userName;

关键字根本不需要编写为第一个属性属性。

别忘了,这并不意味着整个属性是线程安全的。只有 setter/getter 的方法调用是。但是,如果您使用 setter,然后同时使用 2 个不同线程的 getter,它也可能被破坏!

9赞 Ankul Gaur 8/13/2014 #14

如果您在多线程代码中使用您的属性,那么您将能够看到非原子属性和原子属性之间的区别。非原子比原子快,原子是线程安全的,而不是非原子的。

Vijayendra Tripathi 已经给出了一个多线程环境的例子。

15赞 user3693546 7/7/2015 #15

Atomic是线程安全的,它很慢,并且它很好地保证(不保证)无论有多少线程尝试在同一区域上访问,都只提供锁定的值。使用 atomic 时,在此函数中编写的一段代码将成为临界部分的一部分,一次只能执行一个线程。

它只保证了螺纹的安全;它不保证这一点。我的意思是你为你的车聘请了一位专业的司机,但这并不能保证汽车不会发生事故。但是,概率仍然是最小的。

原子 - 它不能被分解,所以结果是意料之中的。使用非原子 - 当另一个线程访问内存区域时,它可以修改它,因此结果是出乎意料的。

代码讨论:

原子使属性线程的 getter 和 setter 安全。例如,如果你写了:

self.myProperty = value;

是线程安全的。

[myArray addObject:@"Abc"] 

不是线程安全的。

评论

0赞 peak 11/26/2016
我不知道最后一段是怎么来的,但很简单,没有“私人副本”这样的东西。
5赞 Laxman Sahni 10/23/2015 #16

原子属性确保保留一个完全初始化的值,无论有多少线程在它上面做 getter 和 setter。

nonatomic 属性指定合成的访问器只是直接设置或返回一个值,不能保证从不同的线程同时访问相同的值时会发生什么。

3赞 Kemo 2/1/2016 #17

原子意味着一次只有一个线程可以访问变量(静态类型)。Atomic是线程安全的,但它很慢。

非原子意味着多个线程可以同时访问变量(动态类型)。非原子是线程不安全的,但它速度很快。

-1赞 Preetha 2/13/2016 #18

如果您使用的是原子,则意味着线程将是安全和只读的。如果你使用的是非原子的,则意味着多个线程访问变量并且是线程不安全的,但它执行速度很快,完成了读写操作;这是一种动态类型。

9赞 mfaani 4/29/2016 #19

开始之前:您必须知道内存中的每个对象都需要从内存中释放,才能产生新的写入器。你不能像在纸上那样简单地在某件事上写字。您必须首先擦除(取消)它,然后才能写入它。如果在擦除完成(或完成一半)并且尚未写入任何内容(或写入一半)并且您尝试阅读它的那一刻可能会非常有问题!原子和非原子可帮助您以不同的方式处理此问题。

先读这个问题,再读Bbum的回答。另外,请阅读我的总结。


原子将永远保证

  • 如果两个不同的人想同时阅读和写作,你的论文不会被烧毁!--> 即使在竞争条件下,您的应用程序也永远不会崩溃。
  • 如果一个人试图写,并且只写了 8 个字母中的 4 个,那么中间就不能读,只有当所有 8 个字母都写完时才能完成阅读 --> 在“仍在写入的线程”上不会发生读取(get),即如果有 8 个字节到字节要写入, 并且只写入了 4 个字节——直到那一刻,您都不允许从中读取。但是既然我说它不会崩溃,那么它将从自动释放对象的值中读取。
  • 如果在写作之前你已经删除了以前写纸上的内容,然后有人想阅读,你仍然可以阅读。如何?您将从类似于 Mac OS 垃圾箱的内容中读取(因为垃圾箱尚未 100% 删除......它处于不确定状态) ---> 如果 ThreadA 要读取,而 ThreadB 已经释放写入,您将从 ThreadB 的最终完全写入值中获取一个值,或者从自动释放池中获取一些值。

保留计数是在 Objective-C 中管理内存的方式。 创建对象时,该对象的保留计数为 1。当您发送时 对象是 Retain 消息,其保留计数递增 1。什么时候 向对象发送释放消息时,其保留计数会递减 作者 1.当您向对象发送自动释放消息时,其保留计数 在将来的某个阶段递减 1。如果对象保留 计数减少到 0,则解除分配。

  • Atomic 不能保证线程安全,尽管它对于实现线程安全很有用。线程安全与你如何编写代码/你从哪个线程队列读取/写入有关。它只保证不可崩溃的多线程。

什么?!多线程和线程安全不同吗?

是的。多线程意味着:多个线程可以同时读取一段共享数据,我们不会崩溃,但这并不能保证您不会从非自动释放的值中读取数据。通过线程安全,可以保证您阅读的内容不会自动释放。 我们之所以不默认将所有东西都原子化,是因为存在性能成本,而且对于大多数事情来说,实际上并不需要线程安全。我们代码的几个部分需要它,对于这几个部分,我们需要使用锁、互斥锁或同步以线程安全的方式编写代码。


nonatomic

  • 由于没有像 Mac OS 垃圾箱这样的东西,所以没有人关心你是否总是得到一个值(<——这可能会导致崩溃),也没有人在乎是否有人试图在你的写作中读到一半(尽管在内存中写一半与在纸上写一半有很大不同,在记忆中它可能会给你一个疯狂的愚蠢值, 而在纸上你只能看到所写内容的一半) --> 不保证不会崩溃,因为它不使用自动释放机制。
  • 不保证可以读取完整的书面值!
  • 比原子快

总体而言,它们在两个方面有所不同:

  • 崩溃与否,因为有或没有自动释放池。

  • 允许在“尚未完成写入或空值”的中间读取,或者不允许并且仅在值完全写入时才允许读取。

-1赞 satisharyan 6/29/2016 #20

原子:通过使用 NSLOCK 锁定线程来确保线程安全。

非原子:由于没有螺纹锁定机制,因此无法确保螺纹安全。

13赞 Proton 7/23/2016 #21

原子(默认)

Atomic 是默认值:如果您不键入任何内容,则您的属性是 原子。原子属性是可以保证的,如果你尝试从 它,您将返回一个有效值。它不作任何保证 关于该值可能是什么,但您将获得良好的数据,而不是 只是垃圾记忆。这允许您做的是,如果您有多个 线程或多个进程指向单个变量,一个 线程可以读取,另一个线程可以写入。如果他们同时击中 时间,则保证读取器线程获取以下两个值之一: 在更改之前或更改之后。原子不行 给你的是关于你这些价值观中的任何一种保证 可能会得到。原子确实经常与线程安全混淆, 这是不正确的。您需要保证您的线安全 其他方式。但是,原子将保证,如果您尝试阅读, 你得到了某种价值。

非原子

另一方面,非原子,正如你可能猜到的那样,只是意味着, “不要做那些原子化的东西。”你失去的是那个保证你 总能拿回一些东西。如果你试图在中间阅读 write,你可以取回垃圾数据。但是,另一方面,你去 快一点。因为原子属性必须做一些神奇的事情 为了保证您将获得一个值,它们会慢一些。如果 这是您经常访问的属性,您可能想放弃 精确到非原子,以确保您不会产生这种速度 罚款。

在此处查看更多信息:https://realm.io/news/tmi-objective-c-property-attributes/

-1赞 Suryanarayan Sahu 9/24/2016 #22

为了简化整个混淆,让我们了解互斥锁。

互斥锁,顾名思义,锁定对象的可变性。因此,如果对象被一个类访问,则没有其他类可以访问同一对象。

在 iOS 中,还提供了互斥锁。现在,它以 FIFO 模式提供服务,并确保流不受共享同一实例的两个类的影响。但是,如果任务位于主线程上,请避免使用原子属性访问对象,因为它可能会占用您的 UI 并降低性能。@sychronise

评论

0赞 Alex Nazarov 3/10/2021
互斥代表“互斥”,而不是“可变”
1赞 paul 12/9/2016 #23

事实是,他们使用自旋锁来实现原子属性。代码如下:

 static inline void reallySetProperty(id self, SEL _cmd, id newValue, 
      ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) 
    {
        id oldValue;
        id *slot = (id*) ((char*)self + offset);

        if (copy) {
            newValue = [newValue copyWithZone:NULL];
        } else if (mutableCopy) {
            newValue = [newValue mutableCopyWithZone:NULL];
        } else {
            if (*slot == newValue) return;
            newValue = objc_retain(newValue);
        }

        if (!atomic) {
            oldValue = *slot;
            *slot = newValue;
        } else {
            spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];
            _spin_lock(slotlock);
            oldValue = *slot;
            *slot = newValue;        
            _spin_unlock(slotlock);
        }

        objc_release(oldValue);
    }
10赞 Shourob Datta 12/13/2016 #24
  • -Atomic 表示只有一个线程访问变量(静态类型)。
  • -Atomic是线程安全的。
  • -但它的性能很慢

如何声明:

由于原子是默认的,

@property (retain) NSString *name;

AND 在实现文件中

self.name = @"sourov";

假设与三个属性相关的任务是

 @property (retain) NSString *name;
 @property (retain) NSString *A;
 @property (retain) NSString *B;
 self.name = @"sourov";

所有属性并行工作(如异步工作)。

如果从线程 A 调用“name”,

同时,如果您调用

[self setName:@"Datta"]

从线程 B 开始,

现在,如果 *name 属性是非原子的,那么

  • 它将返回 A 的值“Datta”
  • 它将返回 B 的值“Datta”

这就是为什么非原子被称为线程不安全的原因,但由于并行执行,它的性能很快

现在,如果 *name 属性是原子的

  • 它将确保 A 的值“Sourov”
  • 然后它将返回 B 的值“Datta”

这就是为什么原子被称为线程安全,这就是为什么它被称为读写安全

这种情况操作将串行执行。而且性能慢

- 非原子意味着多线程访问变量(动态类型)。

- 非原子是线程不安全的。

- 但它的性能很快

-Nonatomic 不是默认行为,我们需要在 property 属性中添加 nonatomic 关键字。

对于 In Swift 确认 Swift 属性在 ObjC 意义上是非原子的。其中一个原因是你要考虑每个属性的原子性是否足以满足你的需求。

参考: https://forums.developer.apple.com/thread/25642

欲了解更多信息,请访问网站 http://rdcworld-iphone.blogspot.in/2012/12/variable-property-attributes-or.html

评论

5赞 Alejandro Iván 2/15/2017
正如许多其他人所说,它不是线程安全的!它更能抵抗线程问题,但不是线程安全的。它只是确保你得到一个完整的值,也就是“正确”的值(二进制级别),但绝不能确保它是你的业务逻辑的当前和“正确”值(它可能是过去的值,按照你的逻辑是无效的)。atomic
-2赞 ashish.surana 8/4/2018 #25

原子属性 :- 当一个变量被分配了原子属性时,这意味着它只有一个线程访问,并且它将是线程安全的,并且在性能方面会很慢,将具有默认行为。

非原子属性 :- 当一个变量被分配了非原子属性时,这意味着它具有多线程访问,并且它不是线程安全的,并且在性能方面会很快,将具有默认行为,当两个不同的线程想要同时访问变量时,它会产生意想不到的结果。

评论

0赞 Peter Cordes 7/7/2022
“只有一个线程访问”听起来不对。例如,原子可以由任意数量的线程并行读取,即使通过MESI缓存一致性,也无需相互序列化(就像存储和原子RMW的情况一样)。读取端可扩展性是无锁原子与读/写器锁相比的主要优势之一。int
9赞 Suraj K Thomas 1/29/2019 #26

原子数 原子(默认)

Atomic 是默认值:如果您不键入任何内容,则您的属性是 原子。原子属性是可以保证的,如果你尝试从 它,您将返回一个有效值。它不作任何保证 关于该值可能是什么,但您将获得良好的数据,而不是 只是垃圾记忆。这允许您做的是,如果您有多个 线程或多个进程指向单个变量,一个 线程可以读取,另一个线程可以写入。如果他们同时击中 时间,则保证读取器线程获取以下两个值之一: 在更改之前或更改之后。原子不行 给你的是关于你这些价值观中的任何一种保证 可能会得到。原子确实经常与线程安全混淆, 这是不正确的。您需要保证您的线安全 其他方式。但是,原子将保证,如果您尝试阅读, 你得到了某种价值。

非原子

另一方面,非原子,正如你可能猜到的那样,只是意味着, “不要做那些原子化的东西。”你失去的是那个保证你 总能拿回一些东西。如果你试图在中间阅读 write,你可以取回垃圾数据。但是,另一方面,你去 快一点。因为原子属性必须做一些神奇的事情 为了保证您将获得一个值,它们会慢一些。如果 这是您经常访问的属性,您可能想放弃 精确到非原子,以确保您不会产生这种速度 罚款。访问

图片由 https://academy.realm.io/posts/tmi-objective-c-property-attributes/

原子性属性属性(原子和非原子)不会反映在相应的 Swift 属性声明中,但是当从 Swift 访问导入的属性时,Objective-C 实现的原子性保证仍然成立。

所以,如果你在 Objective-C 中定义了一个原子属性,当 Swift 使用它时,它将保持原子状态。

礼貌 https://medium.com/@YogevSitton/atomic-vs-non-atomic-properties-crash-course-d11c23f4366c

1赞 Taspiya Maisha 7/3/2021 #27

在一行中:

Atomic是线程安全的。 线程不安全。Nonatomic

评论

1赞 Cristik 7/3/2021
Atomic并不一定保证螺纹安全。考虑向 .虽然访问数组属性是线程安全的,但修改它根本不是线程安全的。@property(atomic) NSArray *names
-1赞 Ranu Dhurandhar 5/3/2023 #28

在 Objective-C 中,原子属性的实现允许从不同的线程安全地读取和写入属性。对于非原子属性,当同时写入新值时,可以释放读取值的基础指针。

原子意味着只有一个线程访问变量(静态类型)。Atomic是线程安全的,但它很慢。非原子意味着多个线程访问变量(动态类型)。非原子是线程不安全的,但它速度很快。