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


Answers:


1761

最后两个是相同的;“原子”是默认的行为(注意,它实际上不是一个关键字,它仅由没有规定nonatomic - atomic在最近版本的LLVM /铛加入关键字)。

假设您正在@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@一起的一起的@@综合的基础上实现,原子的还是非原子的改变了生成的代码。如果您正在编写自己的setter / getter,则原子/非原子/保留/分配/复制仅是建议性的。(注意:@synthesize现在是LLVM的最新版本中的默认行为。也无需声明实例变量;它们也将自动合成,并且将_在其名称前添加前缀以防止意外的直接访问)。

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

在中nonatomic,没有此类保证。因此,nonatomic比“原子”要快得多。

什么“原子”并没有做的就是关于线程安全任何保证。如果线程A同时调用B和C的getter,而线程C使用不同的值调用setter,则线程A可能会获得返回的三个值中的任何一个-在调用任何setter之前的值之一,或者将其中一个值传递给setter同样,对象可能以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在更新从属属性时排除访问。


21
鉴于任何线程安全代码都将执行其自己的锁定等操作,那么何时要使用原子属性访问器?我很难想到一个很好的例子。
丹尼尔·迪基森

8
@bbum很有道理。我喜欢您对另一个答案的评论,即线程安全更多地是模型级别的关注。来自IBM线程安全性定义:ibm.co/yTEbjY “如果正确实现了一个类,这是表示它符合其规范的另一种方式,则没有操作序列(对公共字段的读取或写入以及对公共方法的调用)该类的对象应该能够将其置于无效状态,观察该对象处于无效状态或违反该类的任何不变式,先决条件或后置条件。”
本·弗林

6
这是一个类似于@StevenKramer的示例:我有一个@property NSArray* astronomicalEvents;列出要在UI中显示的数据的示例。当应用程序启动时,指针指向一个空数组时,该应用程序将从Web提取数据。当网络请求完成时(在另一个线程中),应用将构建一个新的数组,然后自动将该属性设置为新的指针值。它是线程安全的,除非我丢失了某些东西,否则我不必编写任何锁定代码。对我来说似乎很有用。
bugloaf

10
@HotLicks另一个有趣的地方;在某些体系结构(不记得是哪一种)上,作为参数传递的64位值可能会在寄存器中传递一半,在堆栈中传递一半。 atomic防止跨线程半值读取。(这是一个有趣的错误,
值得

8
@congliu线程A返回一个没有retain/autorelease跳舞的对象。线程B释放对象。线程A进入繁荣时期atomic确保线程A对返回值具有强引用(+1保留计数)。
bbum 2013年

360

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_;
    }
}

基本上,原子版本必须采取锁定措施以确保线程安全,并且还要在对象上增加引用计数(并通过自动释放计数来平衡它),以确保调用方可以存在该对象,否则如果另一个线程正在设置该值,则是潜在的竞争条件,从而导致引用计数降至0。

实际上,根据属性是标量值还是对象以及保留,复制,只读,非原子等相互作用的方式,这些事物的工作方式存在许多不同的变体。通常,属性合成器只知道如何对所有组合执行“正确的事情”。


8
@Louis Gerbarg:如果您尝试分配同一对象(即:userName == userName_),我相信您的(非原子,保留)设置程序的版本将无法正常工作
Florin 2010年

5
您的代码有些误导;有没有什么原子getter / setter方法是同步的保证。至关重要的是,@property (assign) id delegate;它没有在任何东西上同步(iOS SDK GCC 4.2 ARM -Os),这意味着[self.delegate delegateMethod:self];和之间存在竞争foo.delegate = nil; self.foo = nil; [super dealloc];。见stackoverflow.com/questions/917884/...
TC。

@fyolnish我不知道是什么_val/ val是的,但没有,真的没有。原子copy/ retain属性的getter 需要确保它不会返回其refcount变为零的对象,这是因为setter在另一个线程中被调用,这实质上意味着它需要读取ivar,并在确保setter未被保存的同时保留它。覆盖并释放它,然后自动释放它以平衡保留。这本质上意味着 getter和setter必须使用锁定(如果存储器布局是固定的,应该是可行的与CAS2指令;唉-retain是一个方法调用)。
tc。

@tc已经有一段时间了,但是我想写的可能是这样的:gist.github.com/fjolnir/5d96b3272c6255f6baae但是,是的,读者可以在setFoo之前​​读取旧值:返回,然后在读者将其退回。但是,也许如果setter使用-autorelease而不是-release,那就可以解决此问题。
Fjölnir

@fyolnish不幸的是,没有:它会在setter的线程上自动释放,而它需要在getter的线程上自动释放。由于您正在使用递归,因此看起来堆栈耗尽的可能性很小。
tc。

169

原子

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

非原子

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

137

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

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

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

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


116

该问题的其他出色答案已经很好地定义了语法和语义。由于执行性能不够详细,我将添加我的答案。

这3个功能之间有什么区别?

我一直都将atomic作为默认值感到非常好奇。在我们的抽象层次上,使用原子属性作为类来实现100%线程安全是一种极端的情况。对于真正正确的多线程程序,几乎可以肯定需要程序员的干预。同时,性能特征和执行尚未深入。这些年来,我编写了一些大量的多线程程序,nonatomic由于原子对任何目的都不明智,所以我一直在声明自己的属性。在讨论这个问题的原子和非原子特性的细节时,我进行了一些分析,并遇到了一些奇怪的结果。

执行

好。我要清除的第一件事是锁定实现是实现定义和抽象的。路易(Louis)@synchronized(self)在他的示例中使用-我已经将其视为常见的混乱根源。实现并不实际使用@synchronized(self); 它使用对象级自旋锁。Louis的插图适合使用我们都熟悉的结构进行高级插图,但是重要的是要知道它不使用@synchronized(self)

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

性能

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

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

在有争议的情况下,物体也可能超过50次。

由于实现中存在许多优化和变体,因此很难衡量在这些情况下的实际影响。您可能经常会听到类似“请信任它,除非您进行分析并发现问题”之类的内容。由于抽象级别,实际上很难衡量实际影响。从概要文件收集实际成本可能非常耗时,并且由于抽象,因此非常不准确。同样,ARC vs MRC也可以发挥很大的作用。

因此,让我们退后一步,而不是着重于属性访问的实现,我们将包括常见的可疑对象,例如objc_msgSend,并在一些无争议的情况下(以秒为单位的值)检查一些实际的高级结果,以了解对NSStringgetter的多次调用:

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

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

尽管我非常关注性能,但我还是说语义第一!。同时,对于许多项目而言,性能并不是优先事项。但是,知道执行细节和所用技术的成本肯定不会受到损害。您应该根据需要,目的和能力使用正确的技术。希望这可以节省您几个小时的比较,并帮助您在设计程序时做出更明智的决定。


MRC | 原子| 合成吸气剂:47 ARC | 原子| 合成吸气剂:47是什么使它们相同?ARC是否应该有更多的开销?
SDEZero 2012年

2
因此,如果原子特性不好,则它们为默认值。增加样板代码?
Kunal Balani 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
贾斯汀2013年

您在某处在线进行此测试吗?:我一直在这里将我的github.com/LearnCocos2D/LearnCocos2D/tree/master/...
LearnCocos2D

@ LearnCocos2D,我还没有为人类食用做过准备,对不起。
贾斯汀

95

原子 =线程安全

非原子 =无线程安全

线程安全性:

如果实例变量在从多个线程访问时正确运行,则它们是线程安全的,而不管运行时环境对那些线程的执行进行调度或交织,并且调用代码部分没有其他同步或其他协调。

在我们的上下文中:

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

使用地点atomic

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

的含义atomic

速度并不快,nonatomic因为nonatomic它不需要从运行时对它进行任何看门狗工作。

使用地点nonatomic

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


3
您在这里所说的一切都是正确的,但对于今天的编程,最后一句话实质上是“错误”,Dura。您真想以此方式“改善性能”实在是太不可思议了。(我的意思是,在您达到这一目标之前,您将“不使用ARC”,“因为速度慢而未使用NSString!”等等。)举一个极端的例子,这就像说“团队,不要在代码中添加任何注释,因为它会使我们放慢速度。” 没有现实的开发流程,您可能会出于可靠性的原因而希望(不存在的)理论性能提高。
Fattie 2014年

3
@JoeBlow了一个事实,你可以在这里验证developer.apple.com/library/mac/documentation/Cocoa/Conceptual/...
Durai Amuthan.H

1
FWIW的杜莱(Durai),该链接直接与您的“原子=线程安全”这一论点相矛盾。苹果在文件中明确表示:“属性原子性不是对象线程安全的同义词。” 实际上,atomic不足以实现线程安全。
罗布

69

我发现原子和非原子性的一个很好的解释放在这里。这是来自同一方面的一些相关文本:

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

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

因为atomic变量不能被中断,所以确保它们在任何时候包含的值(线程锁定)都不会损坏,尽管确保此线程锁定会使访问它们的速度变慢。non-atomic另一方面,变量不提供此类保证,但确实提供了快速访问的优势。总结起来,non-atomic当您知道多个线程不会同时访问您的变量并加快速度时,请继续使用。


1
链接断开。;(
罗布

这就是链接的问题:(幸运的是,我在答案中引用了相关文本
tipycalFlow

67

在阅读了很多文章,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 是默认行为
    • 非垃圾回收环境中的原子访问器(即,使用保留/释放/自动释放时)将使用锁来确保另一个线程不会干扰该值的正确设置/获取。
    • 它实际上不是关键字。

    例:

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

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

    例:

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

如何分配和保留/保留都默认?
BangOperator 2016年

强配有ARC,留住了ARC之前默认
abdullahselek

56

原子的

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

如果您想象一下以下功能同时在两个线程上发生,那么您会明白为什么结果会很不好。

-(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
该评论没有多大意义。你能澄清一下吗?如果您在Apple网站上查看示例,则原子关键字将在更新其属性时在该对象上同步。
安德鲁·格兰特

52

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

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

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


8
有两个原因。首先,对于合成代码,它生成速度更快(但不是线程安全代码)。其次,如果您编写的不是非原子的客户访问器,则可以让任何未来的用户在代码未实现时都注释它们不是原子的。
Louis Gerbarg


31

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

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


14

Atomic是线程安全的,它运行缓慢,并且可以很好地确保(不能保证)无论在同一区域上尝试访问多少线程,都仅提供锁定值。使用原子时,在此函数内编写的一段代码成为关键部分的一部分,一次只能在其中执行一个线程。

它仅确保线程安全。它不能保证。我的意思是,您为汽车聘请了专业的驾驶员,但这仍然不能保证汽车不会发生事故。但是,可能性仍然是最小的。

原子-无法分解,因此可以预期结果。使用非原子-当另一个线程访问该内存区域时,它可以对其进行修改,因此结果是意外的。

代码对话:

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

self.myProperty = value;

是线程安全的。

[myArray addObject:@"Abc"] 

不是线程安全的。


我不知道最后一段是怎么来的,但这很简单,没有“私有副本”之类的东西。
达到峰值

13

没有这样的关键字“ atomic”

@property(atomic, retain) UITextField *userName;

我们可以像上面那样使用

@property(retain) UITextField *userName;

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


10
“有这样的关键字”,该关键字默认情况下不是必需的,即使是默认值也不表示该关键字不存在。
Matthijn

4
这是不正确的。关键字确实存在。这个答案具有误导性,我鼓励将其删除。
sethfri 2015年

12

原子的(默认)

原子是默认设置:如果您不输入任何内容,则您的属性是原子的。原子属性可以保证,如果您尝试从中读取内容,则将取回有效值。它不能保证该值是多少,但是您将获得良好的数据,而不仅仅是垃圾内存。这允许您执行的操作是,如果有多个线程或多个进程指向一个变量,则一个线程可以读取而另一个线程可以写入。如果它们同时命中,则保证读取器线程获得两个值之一:更改之前或更改之后。原子不会给您任何保证,您可能会获得其中哪些值。原子通常确实与线程安全混淆,这是不正确的。您需要以其他方式保证线程安全。

非原子的

另一方面,您可能会猜到,非原子意味着“不要做原子的事情”。您所失去的是保证您总是能得到一些回报。如果尝试在写入过程中进行读取,则可能会获取垃圾数据。但是,另一方面,您的运行速度更快。因为原子属性必须做一些魔术才能保证您将获得一个值,所以它们要慢一些。如果这是您经常访问的属性,则可能需要降低为非原子属性,以确保不会造成速度损失。

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


11

默认情况下atomic,这意味着它无论您何时使用属性花费你的表现,但它是线程安全的。Objective-C的作用是设置一个锁,因此,只要执行了setter / getter,只有实际线程才能访问该变量。

具有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和具有两个不同线程的getter,则它也可能会损坏!


10

开始之前:您必须知道内存中的每个对象都需要从内存中重新分配,以使新的编写器发生。您不能像在纸上一样简单地在某些东西上写字。您必须先擦除(取消分配)它,然后才能对其进行写入。如果目前擦除已完成(或已完成一半),尚未写入任何内容(或已写入一半),而您尝试读取它,则可能会很成问题!原子和非原子可帮助您以不同方式解决此问题。

首先阅读问题,然后阅读Bbum的答案。另外,请阅读我的总结。


atomic 将始终保证

  • 如果两个不同的人想同时阅读和写作,那么您的论文将不会燃烧!->即使在竞争条件下,您的应用程序也绝不会崩溃。
  • 如果一个人试图写,并且只写了8个字母中的4个,那么中间没有人可以阅读,只有当所有8个字母都写完之后才能进行阅读->不会发生read(get) “仍在写入的线程”,即如果要写入的字节数为8个字节,而仅写入4个字节,则直到此时为止,您都不允许从中读取。但是由于我说过它不会崩溃,所以它将从自动释放对象的值中读取。
  • 如果之前已经擦除了以前写在纸上的东西,然后有人想要阅读,您仍然可以阅读。怎么样?您将从类似Mac OS的垃圾箱中读取内容(由于垃圾箱仍未100%擦除...处于困境中)--->如果在ThreadB已经被释放分配进行写入的同时读取ThreadA,您将得到从ThreadB最终完全写入的值中获取一个值,或从自动释放池中获取某些值。

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

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

什么?!多线程和线程安全性是否不同?

是。多线程意味着:多个线程可以同时读取一个共享的数据,并且我们不会崩溃,但这并不保证您不会从非自动释放的值中读取数据。借助线程安全性,可以确保您阅读的内容不会自动发布。我们默认情况下不使所有内容原子化的原因是,这会降低性能,并且在大多数情况下实际上并不需要线程安全性。我们的代码的某些部分需要它,而对于那几部分,我们需要使用锁,互斥或同步以线程安全的方式编写代码。


nonatomic

  • 由于没有像Mac OS Trash Bin这样的东西,所以没有人关心您是否总是获得价值(<-这可能导致崩溃),也没有人关心是否有人尝试在写作过程中进行阅读(尽管在内存中进行中途写作与在纸上进行中途写作有很大不同,在内存中它可能会给您一个从前的疯狂愚蠢的价值,而在纸上您只会看到所写内容的一半)->不保证不会崩溃,因为它不使用自动释放机制。
  • 不保证阅读完整的书面价值!
  • 比原子快

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

  • 是否因没有自动释放池而崩溃。

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


9

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

Vijayendra Tripathi已经给出了多线程环境的示例。


9
  • -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调用“名称” ,

同时拨打

[self setName:@"Datta"]

从线程B开始

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

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

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

现在,如果* name属性是atomic

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

这就是为什么atomic称为线程安全,也 就是为什么它称为读写安全的原因

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

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

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

-但性能很快

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

对于In Swift,确认ObjC意义上的Swift属性是非原子的。原因之一是您要考虑按属性原子性是否足以满足您的需求。

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

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


4
正如许多其他许多人所说的那样,atomic不是线程安全的!它更能抵抗线程问题,但不是线程安全的。它只是确保您将获得一个完整的值,也就是一个“正确的”值(二进制级别),但绝不会确保它是您的业务逻辑的当前值和“正确”值(它可以是过去的值,根据您的逻辑无效)。
亚历杭德罗·伊万

6

原子原子性(默认)

原子是默认设置:如果您不输入任何内容,则您的属性是原子的。原子属性可以保证,如果您尝试从中读取内容,则将取回有效值。它不能保证该值是多少,但是您将获得良好的数据,而不仅仅是垃圾内存。这允许您执行的操作是,如果有多个线程或多个进程指向一个变量,则一个线程可以读取而另一个线程可以写入。如果它们同时命中,则保证读取器线程获得两个值之一:更改之前或更改之后。原子不会给您任何保证,您可能会获得其中哪些值。原子通常确实与线程安全混淆,这是不正确的。您需要以其他方式保证线程安全。

非原子的

另一方面,您可能会猜到,非原子意味着“不要做原子的事情”。您所失去的是保证您总是能得到一些回报。如果尝试在写入过程中进行读取,则可能会获取垃圾数据。但是,另一方面,您的运行速度更快。因为原子属性必须做一些魔术才能保证您将获得一个值,所以它们要慢一些。如果这是您经常访问的属性,则可能需要降低为非原子属性,以确保不会造成速度损失。访问

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提供


5

无论有多少线程正在执行getter和setter方法,atomic属性都确保保留完全初始化的值。

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


3

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

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


1

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


1

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

 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);
    }

0

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

互斥锁,按名称,锁定对象的可变性。因此,如果对象由类访问,则其他任何类都不能访问同一对象。

在iOS中,@sychronise还提供了互斥锁。但是,如果任务在主线程上,请避免使用原子属性访问对象,因为它可能会占用您的UI并降低性能。


-1

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

非原子的:由于没有线程锁定机制,因此不能确保线程安全。


-1

原子属性:-当一个分配了原子属性的变量时,它仅具有一个线程访问权限,并且将是线程安全的,并且在性能方面具有良好的表现,它将具有默认行为。

非原子属性:-当一个分配了原子属性的变量意味着它具有多线程访问权限,并且不是线程安全的并且性能下降时,将具有默认行为,并且两个不同的线程希望同时访问变量它会产生意想不到的结果。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.