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

在属性声明中atomicnonatomic是什么意思?

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

这三者在操作上有什么区别?

522032 次浏览

原子:

原子保证以原子方式执行对属性的访问。例如。它总是返回一个完全初始化的对象,一个线程上属性的任何获取/设置必须在另一个线程访问它之前完成。

如果您想象以下函数同时发生在两个线程上,您可以看到为什么结果不会很漂亮。

-(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;}
...}

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

<强>缺点:性能下降,执行速度变慢

非原子:

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

优点:速度极快

<强>缺点:多线程情况下垃圾值的机会。

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

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

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

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

请注意,没有“原子”关键字,如果您不指定“非原子”,则该属性是原子的,但显式指定“原子”将导致错误。

如果您不指定“非原子”,则该属性是原子的,但如果您愿意,您仍然可以在最新版本中显式指定“原子”。

//@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计数(以及自动释放计数以平衡它),以便保证对象对调用者存在,否则如果另一个线程设置该值,则存在潜在的竞争条件,导致ref计数降至0。

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

最后两个是相同的;“原子”是默认行为(请注意,它实际上不是一个关键字;它仅由#0的缺失指定-atomic是在最近版本的llvm编译器/clang中作为关键字添加的)。

假设你正在@合成方法实现,原子与非原子会更改生成的代码。如果你正在编写自己的设置器/获取器,原子/非原子/保留/分配/复制只是建议。(注意:@合成现在是最新版本LLVM的默认行为。也不需要声明实例变量;它们也会自动合成,并且它们的名称前面会有一个_,以防止意外的直接访问)。

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

nonatomic中,没有这样的保证。因此,nonatomic比“原子”快得多。

“原子”没有所做的是对线程安全做出任何保证。如果线程A与线程B同时调用getter,而线程C使用不同的值调用setter,则线程A可能会获得返回的三个值中的任何一个-在调用任何setter之前的值或传递给B和C中的setter的值。同样,对象最终可能会得到来自B或C的值,无从得知。

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

添加到这个:

单个属性的atomicity也不能保证多个依赖属性在起作用时的线程安全。

考虑:

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

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

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

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

@property(atomic, retain) UITextField *userName;

我们可以使用上面的喜欢

@property(retain) UITextField *userName;

请参阅堆栈溢出问题如果我使用@属性(原子,保留)NSString*myString,我会遇到问题

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

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

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

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

我找到了一个关于原子和非原子属性这里的很好的解释。这里有一些相关的文本:

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

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

因为atomic变量不能被中断,所以它们在任何时候包含的值(线程锁)都保证是没有腐败,尽管确保这个线程锁会降低对它们的访问速度。另一方面,non-atomic变量没有这样的保证,但确实提供了更快访问的奢侈。总而言之,当你知道你的变量不会被多个线程同时访问并加快速度时,使用non-atomic

原子

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

非原子

  • 不是默认行为
  • 更快(对于合成代码,即对于使用@属性和@合成创建的变量)
  • 不是线程安全的
  • 当两个不同的进程同时访问同一个变量时,可能会导致意外行为

语法和语义学已经被这个问题的其他优秀答案很好地定义了。因为执行性能没有详细说明,我将添加我的答案。

这三者之间的功能区别是什么?

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

执行

好的。我想澄清的第一件事是锁定实现是实现定义和抽象的。路易斯在他的示例中使用了@synchronized(self)-我认为这是一个常见的混淆来源。实现没有实际上使用@synchronized(self);它使用对象级别旋转锁。路易斯的插图对于使用我们都熟悉的构造的高级插图很有好处,但重要的是要知道它不使用@synchronized(self)

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

性能

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

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

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

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

所以让我们退后一步,没有专注于属性访问的实现,我们将包括像objc_msgSend这样的常见嫌疑人,并检查一些现实世界的高级结果,用于在无争议情况下多次调用NSString getter(以秒为单位的值):

  • MRC|非原子|手动实现的getter: 2
  • MRC|非原子|合成吸气剂: 7
  • MRC|原子|合成吸气剂: 47
  • ARC|非原子|合成getter: 38(注意:ARC在这里增加了ref计数循环)
  • ARC|原子|合成吸气剂: 47

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

虽然我很关注性能,但我还是要说0。同时,性能在许多项目中不是很重要。然而,了解执行细节和所用技术的成本当然没有坏处。你应该根据自己的需求、目的和能力使用正确的技术。希望这能节省你几个小时的比较,并帮助你在设计程序时做出更明智的决定。

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

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

在阅读了这么多文章、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. #0

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

    示例:

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

    • nonatomic means multiple thread access the variable (dynamic type).
    • nonatomic is thread-unsafe.
    • But it is fast in performance
    • nonatomic is NOT default behavior. We need to add the nonatomic keyword in the property attribute.
    • It may result in unexpected behavior, when two different process (threads) access the same variable at the same time.

    Example:

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

原子=线程安全

非原子=没有线程安全

线程安全:

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

在我们的背景下:

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

在哪里使用atomic

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

atomic的含义:

没有nonatomic那么快,因为nonatomic不需要任何看门狗在运行时处理它。

在哪里使用nonatomic

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

默认atomic,这意味着无论何时使用该属性都会降低性能,但它是线程安全的。Objective-C所做的是设置锁,因此只有实际线程可以访问变量,只要执行setter/getter。

使用ivar_internal属性的MRC示例:

[_internal lock]; //lockid 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和getter以及两个不同的线程,它也可能被破坏!

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

Vijayendra Tripath i已经给出了一个多线程环境的示例。

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

它只保证线程安全;它不保证。我的意思是,你雇了一个专业的司机为你的车,仍然不能保证汽车不会遇到事故。然而,概率仍然是最小的。

原子-它不能被分解,所以结果是预期的。对于非原子-当另一个线程访问内存区域时,它可以修改它,所以结果是意想不到的。

代码对话:

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

self.myProperty = value;

是线程安全的。

[myArray addObject:@"Abc"]

不是线程安全的。

原子属性确保保留完全初始化的值,无论有多少线程在其上执行getter和setter。

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

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

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

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

在开始之前:你必须知道内存中的每个对象都需要从内存中释放出来,以便发生新的写入器。你不能像在纸上那样简单地写在某物的顶部。你首先擦除(释放)它,然后你可以在它上写入。如果在擦除完成(或一半完成)并且没有写入(或一半写入)尚未的那一刻,你试图读取它可能会非常有问题!原子和非原子帮助你以不同的方式处理这个问题。

首先阅读这个问题,然后阅读Bbum的回答。此外,然后阅读我的摘要。


atomic永远保证

  • 如果两个不同的人想同时读和写,你的论文不仅会烧毁!-->你的应用程序永远不会崩溃,即使在竞争条件下。
  • 如果一个人正在尝试写入并且只写了8个字母中的4个,那么没有人可以在中间读取,只有在所有8个字母都写入时才能读取-->'一个仍在写入的线程'上不会发生无读取(get),即如果有8个字节要写入,并且只有4个字节被写入----到那一刻,你不允许从中读取。但既然我说它不会崩溃,那么它会从自动发布对象的值中读取。
  • 如果之前写你擦除了之前写在纸上的东西,然后有人想读你可以仍然在读。怎么做?你将从类似于Mac OS垃圾箱的东西中读取(因为垃圾箱不是100%被擦除……它处于边缘) ---> 如果ThreadA要读取,而ThreadB已经释放写入,你将从ThreadB最终完全写入的值中获得一个值,或者从自动释放池中获得一些值。

保留计数是Objective-C中管理内存的方式。当您创建一个对象时,它的保留计数为1。当您发送如果对象是保留消息,则其保留计数将增加1。当你向一个对象发送一个释放消息,它的保留计数会递减当您发送一个对象自动释放消息时,它的保留计数在未来的某个阶段递减1。如果对象保留计数减少到0,它被释放。

  • 原子不要保证线程安全,尽管它对实现线程安全很有用。线程安全相对于您编写代码的方式/您正在从哪个线程队列中读取/写入。它只保证不可崩溃的多线程。

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

是的。多线程意味着:多个线程可以同时读取一条共享的数据,并且我们不会崩溃,但它并不能保证你不会从非自动释放的值中读取。使用线程安全,可以保证你读取的不是自动释放的。默认情况下,我们没有将所有内容都设为原子的原因是,存在性能成本,并且对于大多数事情来说并不真正需要线程安全。我们代码的一小部分需要它,对于这一小部分,我们需要使用锁、互斥锁或同步以线程安全的方式编写代码。


nonatomic

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

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

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

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

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

非原子:不确保线程安全,因为没有线程锁定机制。

原子(默认)

原子是默认值:如果你不键入任何内容,你的属性是原子。保证原子属性,如果您尝试从它,你会得到一个有效的值。它不做任何保证关于这个值可能是什么,但你会得到好的数据,而不是只是垃圾内存。这允许你做的是,如果你有多个线程或多个进程指向单个变量,一个线程可以读取,另一个线程可以写入。如果它们同时命中时间,读取器线程保证获得以下两个值之一:在变化之前或变化之后。什么原子不给你的是任何形式的保证关于哪些价值观你原子通常与线程安全混淆,这是不对的你需要保证你的线程安全其他方式。然而,原子将保证,如果你尝试阅读,#36825;有一定的价值

非原子

另一方面,非原子,正如你可能猜到的,只是意味着,“不要做那些原子的东西。”你失去的是保证你总是能得到一些东西。如果你试图在中间阅读写,你可以得到垃圾数据。但是,另一方面,你去再快一点,因为原子属性必须要有魔力为了保证你会得到一个值,它们有点慢。如果这是一个属性,你正在访问很多,你可能想放弃降低到非原子级以确保你不会产生那种速度罚款。

查看更多:https://realm.io/news/tmi-objective-c-property-attributes/

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

根据名称,互斥锁锁定对象的可变性。因此,如果对象被类访问,则没有其他类可以访问相同的对象。

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

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

 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);}
  • -原子意味着只有一个线程访问变量(静态类型)。
  • 原子是线程安全的。
  • -但它的性能很慢

如何申报:

由于原子是默认的,

@property (retain) NSString *name;

AND在实现文件中

self.name = @"sourov";

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

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

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

如果你从线程一个调用“name”,

同时如果你打电话

[self setName:@"Datta"]

从线程B

现在如果*name属性是非原子的然后

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

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

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

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

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

这种情况操作将串行执行。表现缓慢

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

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

-但它的性能很快

-非原子不是默认行为,我们需要在属性属性中添加非原子关键字。

在Swift中确认Swift属性在ObjC意义上是非原子的。一个原因是让您考虑每个属性的原子性是否足以满足您的需求。

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

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

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

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

原子城原子(默认)

原子是默认值:如果你不键入任何内容,你的属性是原子。保证原子属性,如果您尝试从它,你会得到一个有效的值。它不做任何保证关于这个值可能是什么,但你会得到好的数据,而不是只是垃圾内存。这允许你做的是,如果你有多个线程或多个进程指向单个变量,一个线程可以读取,另一个线程可以写入。如果它们同时命中时间,读取器线程保证获得以下两个值之一:在变化之前或变化之后。什么原子不给你的是任何形式的保证关于哪些价值观你原子通常与线程安全混淆,这是不对的你需要保证你的线程安全其他方式。然而,原子将保证,如果你尝试阅读,#36825;有一定的价值

非原子

另一方面,非原子,正如你可能猜到的,只是意味着,“不要做那些原子的东西。”你失去的是保证你总是能得到一些东西。如果你试图在中间阅读写,你可以得到垃圾数据。但是,另一方面,你去再快一点,因为原子属性必须要有魔力为了保证你会得到一个值,它们有点慢。如果这是一个属性,你正在访问很多,你可能想放弃降低到非原子级以确保你不会产生那种速度罚款。访问

礼貌https://academy.realm.io/posts/tmi-objective-c-property-attributes/

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

所以——如果你在Objective-C中定义了一个原子属性,它在被Swift使用时仍然是原子的。

礼貌https://medium.com/@YogevSitton

在一个单一的行:

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