Answers:
最后两个是相同的;“原子”是默认的行为(注意,它实际上不是一个关键字,它仅由没有规定 - 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
在更新从属属性时排除访问。
@property NSArray* astronomicalEvents;
列出要在UI中显示的数据的示例。当应用程序启动时,指针指向一个空数组时,该应用程序将从Web提取数据。当网络请求完成时(在另一个线程中),应用将构建一个新的数组,然后自动将该属性设置为新的指针值。它是线程安全的,除非我丢失了某些东西,否则我不必编写任何锁定代码。对我来说似乎很有用。
atomic
防止跨线程半值读取。(这是一个有趣的错误,
retain/autorelease
跳舞的对象。线程B释放对象。线程A进入繁荣时期。 atomic
确保线程A对返回值具有强引用(+1保留计数)。
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。
实际上,根据属性是标量值还是对象以及保留,复制,只读,非原子等相互作用的方式,这些事物的工作方式存在许多不同的变体。通常,属性合成器只知道如何对所有组合执行“正确的事情”。
@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/...
_val
/ val
是的,但没有,真的没有。原子copy
/ retain
属性的getter 需要确保它不会返回其refcount变为零的对象,这是因为setter在另一个线程中被调用,这实质上意味着它需要读取ivar,并在确保setter未被保存的同时保留它。覆盖并释放它,然后自动释放它以平衡保留。这本质上意味着既 getter和setter必须使用锁定(如果存储器布局是固定的,应该是可行的与CAS2指令;唉-retain
是一个方法调用)。
理解差异的最佳方法是使用以下示例。
假设有一个名为“ 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仍可以并行执行。
该问题的其他出色答案已经很好地定义了语法和语义。由于执行和性能不够详细,我将添加我的答案。
这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
,并在一些无争议的情况下(以秒为单位的值)检查一些实际的高级结果,以了解对NSString
getter的多次调用:
您可能已经猜到,引用计数活动/循环是原子和ARC的重要贡献者。您还会在有争议的案例中看到更大的差异。
尽管我非常关注性能,但我还是说语义第一!。同时,对于许多项目而言,性能并不是优先事项。但是,知道执行细节和所用技术的成本肯定不会受到损害。您应该根据需要,目的和能力使用正确的技术。希望这可以节省您几个小时的比较,并帮助您在设计程序时做出更明智的决定。
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
。
原子 =线程安全
非原子 =无线程安全
如果实例变量在从多个线程访问时正确运行,则它们是线程安全的,而不管运行时环境对那些线程的执行进行调度或交织,并且调用代码部分没有其他同步或其他协调。
如果线程更改了实例的值,则更改后的值可用于所有线程,并且一次只能有一个线程更改该值。
atomic
:如果要在多线程环境中访问实例变量。
atomic
:速度并不快,nonatomic
因为nonatomic
它不需要从运行时对它进行任何看门狗工作。
nonatomic
:如果实例变量不会被多个线程更改,则可以使用它。它提高了性能。
我发现原子和非原子性的一个很好的解释放在这里。这是来自同一方面的一些相关文本:
“原子”意味着它不能被分解。用OS /编程术语来说,原子函数调用是不能中断的-整个函数必须执行,并且直到完成后,才通过OS的常规上下文切换将其交换出CPU。以防万一您不知道:由于CPU一次只能做一件事,因此OS会在很短的时间范围内将对CPU的访问权轮流转给所有正在运行的进程,给人一种错觉多任务处理。CPU调度程序可以(并且确实)在执行的任何时刻中断进程,即使在中间函数调用中也是如此。因此,对于诸如更新共享计数器变量之类的操作(其中两个进程可以尝试同时更新变量),必须“以原子方式”执行它们,即,每个更新操作必须完整完成,然后才能将任何其他进程交换到该进程上。中央处理器。
因此,我猜想在这种情况下,atomic意味着属性读取器方法无法被中断-实际上意味着该方法读取的变量无法在途中更改其值,因为其他一些线程/调用/函数得到了交换到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属性中添加关键字。例:
@property (nonatomic, retain) NSString *name;
@synthesize name;
原子保证对属性的访问将以原子方式执行。例如,它总是返回完全初始化的对象,一个线程上属性的任何获取/设置必须先完成,然后另一个线程才能访问它。
如果您想象一下以下功能同时在两个线程上发生,那么您会明白为什么结果会很不好。
-(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不同,它不能确保每次都完全初始化对象。
优点: 执行速度极快。
缺点: 在多线程的情况下,可能会产生垃圾值。
首先最简单的答案:您的后两个示例之间没有区别。默认情况下,属性访问器是原子的。
非垃圾回收环境中的原子访问器(即,使用保留/释放/自动释放时)将使用锁来确保另一个线程不会干扰该值的正确设置/获取。
有关创建更多信息以及创建多线程应用程序时的其他注意事项,请参阅Apple的Objective-C 2.0文档的“ 性能和线程 ”部分。
Atomic是线程安全的,它运行缓慢,并且可以很好地确保(不能保证)无论在同一区域上尝试访问多少线程,都仅提供锁定值。使用原子时,在此函数内编写的一段代码成为关键部分的一部分,一次只能在其中执行一个线程。
它仅确保线程安全。它不能保证。我的意思是,您为汽车聘请了专业的驾驶员,但这仍然不能保证汽车不会发生事故。但是,可能性仍然是最小的。
原子-无法分解,因此可以预期结果。使用非原子-当另一个线程访问该内存区域时,它可以对其进行修改,因此结果是意外的。
代码对话:
原子使属性线程的getter和setter安全。例如,如果您写过:
self.myProperty = value;
是线程安全的。
[myArray addObject:@"Abc"]
不是线程安全的。
没有这样的关键字“ atomic”
@property(atomic, retain) UITextField *userName;
我们可以像上面那样使用
@property(retain) UITextField *userName;
请参阅堆栈溢出问题,如果我使用@property(atomic,retain)NSString * myString,则会遇到问题。
原子的(默认)
原子是默认设置:如果您不输入任何内容,则您的属性是原子的。原子属性可以保证,如果您尝试从中读取内容,则将取回有效值。它不能保证该值是多少,但是您将获得良好的数据,而不仅仅是垃圾内存。这允许您执行的操作是,如果有多个线程或多个进程指向一个变量,则一个线程可以读取而另一个线程可以写入。如果它们同时命中,则保证读取器线程获得两个值之一:更改之前或更改之后。原子不会给您任何保证,您可能会获得其中哪些值。原子通常确实与线程安全混淆,这是不正确的。您需要以其他方式保证线程安全。
非原子的
另一方面,您可能会猜到,非原子意味着“不要做原子的事情”。您所失去的是保证您总是能得到一些回报。如果尝试在写入过程中进行读取,则可能会获取垃圾数据。但是,另一方面,您的运行速度更快。因为原子属性必须做一些魔术才能保证您将获得一个值,所以它们要慢一些。如果这是您经常访问的属性,则可能需要降低为非原子属性,以确保不会造成速度损失。
在此处查看更多信息:https : //realm.io/news/tmi-objective-c-property-attributes/
在默认情况下是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,则它也可能会损坏!
开始之前:您必须知道内存中的每个对象都需要从内存中重新分配,以使新的编写器发生。您不能像在纸上一样简单地在某些东西上写字。您必须先擦除(取消分配)它,然后才能对其进行写入。如果目前擦除已完成(或已完成一半),但尚未写入任何内容(或已写入一半),而您尝试读取它,则可能会很成问题!原子和非原子可帮助您以不同方式解决此问题。
首先阅读此问题,然后阅读Bbum的答案。另外,请阅读我的总结。
atomic
将始终保证
保留计数是在Objective-C中管理内存的方式。创建对象时,其保留计数为1。向对象发送保留消息时,其保留计数将递增1。向对象发送释放消息时,其保留计数将减1。向对象发送自动释放消息,其保留计数在将来的某个阶段递减1。如果对象的保留计数减少到0,则将其释放。
什么?!多线程和线程安全性是否不同?
是。多线程意味着:多个线程可以同时读取一个共享的数据,并且我们不会崩溃,但这并不保证您不会从非自动释放的值中读取数据。借助线程安全性,可以确保您阅读的内容不会自动发布。我们默认情况下不使所有内容原子化的原因是,这会降低性能,并且在大多数情况下实际上并不需要线程安全性。我们的代码的某些部分需要它,而对于那几部分,我们需要使用锁,互斥或同步以线程安全的方式编写代码。
nonatomic
总体而言,它们在两个方面有所不同:
是否因没有自动释放池而崩溃。
允许在“尚未完成的写入或空值”中间进行读取,或者不允许并且仅在值完全写入后才允许读取。
如果您在多线程代码中使用属性,那么您将能够看到非原子属性和原子属性之间的区别。非原子比原子更快,并且原子是线程安全的,而不是非原子的。
Vijayendra Tripathi已经给出了多线程环境的示例。
如何申报:
由于默认是原子的,
@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属性是非原子的,则
这就是为什么非原子级称为线程不安全的原因,但是由于并行执行,它的性能很快
现在,如果* name属性是atomic
这就是为什么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
atomic
它不是线程安全的!它更能抵抗线程问题,但不是线程安全的。它只是确保您将获得一个完整的值,也就是一个“正确的”值(二进制级别),但绝不会确保它是您的业务逻辑的当前值和“正确”值(它可以是过去的值,根据您的逻辑无效)。
原子原子性(默认)
原子是默认设置:如果您不输入任何内容,则您的属性是原子的。原子属性可以保证,如果您尝试从中读取内容,则将取回有效值。它不能保证该值是多少,但是您将获得良好的数据,而不仅仅是垃圾内存。这允许您执行的操作是,如果有多个线程或多个进程指向一个变量,则一个线程可以读取而另一个线程可以写入。如果它们同时命中,则保证读取器线程获得两个值之一:更改之前或更改之后。原子不会给您任何保证,您可能会获得其中哪些值。原子通常确实与线程安全混淆,这是不正确的。您需要以其他方式保证线程安全。
非原子的
另一方面,您可能会猜到,非原子意味着“不要做原子的事情”。您所失去的是保证您总是能得到一些回报。如果尝试在写入过程中进行读取,则可能会获取垃圾数据。但是,另一方面,您的运行速度更快。因为原子属性必须做一些魔术才能保证您将获得一个值,所以它们要慢一些。如果这是您经常访问的属性,则可能需要降低为非原子属性,以确保不会造成速度损失。访问
由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提供
无论有多少线程正在执行getter和setter方法,atomic属性都确保保留完全初始化的值。
nonatomic属性指定合成的访问器仅直接设置或返回一个值,而不保证如果从不同的线程同时访问相同的值会发生什么。
事实是,他们使用自旋锁来实现原子属性。代码如下:
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);
}
为了简化整个混乱,让我们了解互斥锁。
互斥锁,按名称,锁定对象的可变性。因此,如果对象由类访问,则其他任何类都不能访问同一对象。
在iOS中,@sychronise
还提供了互斥锁。但是,如果任务在主线程上,请避免使用原子属性访问对象,因为它可能会占用您的UI并降低性能。
原子属性:-当一个分配了原子属性的变量时,它仅具有一个线程访问权限,并且将是线程安全的,并且在性能方面具有良好的表现,它将具有默认行为。
非原子属性:-当一个分配了原子属性的变量意味着它具有多线程访问权限,并且不是线程安全的并且性能下降时,将具有默认行为,并且两个不同的线程希望同时访问变量它会产生意想不到的结果。