@property在Objective-C中的retain、assign、copy和nonatomic是什么意思?

230
作为一个对Objective-C新手,有人能给我概述一下在@property指令后面的retain、assign、copy以及其他我可能漏掉的内容吗?它们都是做什么的,为什么我会想要使用其中之一而不是其他的呢?

1
苹果公司将其称为“属性”或“属性属性”。 - nevan king
5个回答

312

在了解@property的属性之前,您应该知道@property的用途。

  • @property提供了一种定义类所封装信息的方式。如果使用@property声明一个对象/变量,则其他导入该类的类将能够访问该对象/变量。

  • 如果在头文件中使用@property声明一个对象,则必须在实现文件中使用@synthesize合成它。这使得该对象成为KVC兼容的。默认情况下,编译器会为该对象合成访问器方法

  • 访问器方法包括:setter和getter。

例如: .h

@interface XYZClass : NSObject
@property (nonatomic, retain) NSString *name;
@end

.m

@implementation XYZClass
@synthesize name;
@end

现在编译器将为name合成访问方法。
XYZClass *obj=[[XYZClass alloc]init];
NSString *name1=[obj name]; // get 'name'
[obj setName:@"liza"]; // first letter of 'name' becomes capital in setter method
  • @property 的属性列表

    atomic, nonatomic, retain, copy, readonly, readwrite, assign, strong, getter=method, setter=method, unsafe_unretained

  • atomic是默认行为。如果一个对象被声明为原子的,那么它就变成了线程安全的。线程安全意味着,在同一时间内,该类的特定实例只有一个线程可以控制该对象。

如果线程正在执行getter方法,那么其他线程不能在该对象上执行setter方法。这会使程序运行变慢。

@property NSString *name; //by default atomic`
@property (atomic)NSString *name; // explicitly declared atomic`
  • nonatomic 不是线程安全的。你可以使用nonatomic属性来指定合成访问器只是直接设置或返回一个值,没有保证如果这个同样的值同时从不同的线程访问会发生什么。

因此,访问nonatomic属性比atomic属性更快。

@property (nonatomic)NSString *name;   
  • 当属性是指向对象的指针时,需要使用retain

设置方法将增加对象的保留计数,这样它将占用自动释放池中的内存。

@property (retain)NSString *name;
  • 拷贝如果使用拷贝,就不能使用保留。使用类的拷贝实例将包含其自己的副本。

即使设置了可变字符串并随后更改了它,该实例也会捕获其在设置时具有的任何值。不会合成setter和getter方法。

@property (copy) NSString *name;

现在,

NSMutableString *nameString = [NSMutableString stringWithString:@"Liza"];    
xyzObj.name = nameString;    
[nameString appendString:@"Pizza"]; 

name不会受到影响。

  • readonly 如果你不想通过setter方法更改属性,可以声明该属性为只读。

编译器将生成getter但不生成setter。

@property (readonly) NSString *name;
  • readwrite 是默认行为。您不需要显式指定 readwrite 属性。

它与 readonly 相反。

@property (readwrite) NSString *name;
  • assign会生成一个setter,它直接将值分配给实例变量,而不是复制或保留它。这对于像NSInteger和CGFloat这样的原始类型或者您没有直接拥有的对象(例如代理)最为适用。

请记住,在启用垃圾回收时,retain和assign基本上是可以互换的。

@property (assign) NSInteger year;
  • strong是retain的替代品。

它随着ARC一起使用。

@property (nonatomic, strong) AVPlayer *player; 
  • getter=method 如果您想为getter方法使用不同的名称,则可以通过向属性添加属性来指定自定义名称。

对于布尔属性(具有YES或NO值的属性),习惯上getter方法以“is”开头。

@property (getter=isFinished) BOOL finished;
  • setter=method 如果您想为setter方法使用不同的名称,可以通过向属性添加属性来指定自定义名称。

该方法应以冒号结尾。

@property(setter = boolBool:) BOOL finished;
  • unsafe_unretained 在Cocoa和Cocoa Touch中有一些类尚不支持弱引用,这意味着您不能声明一个弱属性或弱局部变量来跟踪它们。这些类包括NSTextView、NSFont和NSColorSpace等。如果需要使用弱引用来访问这些类中的一个,您必须使用unsafe reference。

unsafe reference类似于weak reference,因为它不会保留相关对象的存活,但如果目标对象被释放,它不会被设置为nil

@property (unsafe_unretained) NSObject *unsafeProperty;

如果您需要指定多个属性,只需将它们作为逗号分隔的列表包含在内,如下所示:
@property (readonly, getter=isFinished) BOOL finished;

此外,弱引用意味着所引用的对象没有引用计数,但仍然被引用或者根本没有被引用。这有点像“是的,有东西引用了我”和“有9个引用指向我”(这就是强引用的情况)。 - Alex Zavatone
6
请忽略答案中关于垃圾回收的部分,因为根据苹果文档,在Mac OS X中已经不再支持垃圾回收,在iOS中也不存在垃圾回收。 - Basil Bourque
4
注意:属性的原子性与对象的线程安全并不是同义词。 - jk7
1
如果你在头文件中使用 @property 声明一个对象,那么你在实现文件中就必须使用 @synthesize 来合成它。但并非总是这样。例如,“默认情况下,一个readwrite属性将由一个实例变量支持,并且该实例变量将由编译器自动合成。”来源于文档 - Franklin Yu
4
@liza 这是一个很好的回答。为什么它不被接受呢?它比当前被接受的回答传达了更丰富的知识性解释。我有时候不太理解 StackOverflow。 - Charles Robertson
显示剩余3条评论

284

MrMage提供的文章链接已不再有效。以下是我在编写Objective-C时学到的一些知识:

nonatomic vs. atomic - "atomic"是默认值。始终使用“nonatomic”。我不知道为什么,但我读过的这本书上说“很少有理由”使用“atomic”。(顺便说一下:我读的是BNR的“iOS编程”书。)

readwrite vs. readonly - "readwrite"是默认值。当您@synthesize时,会为您创建getter和setter。如果您使用“readonly”,则不会创建setter。将其用于您不希望在对象实例化后更改的值。

retain vs. copy vs. assign

  • "assign"是默认值。在由@synthesize创建的setter中,该值将简单地分配给属性。我的理解是,“assign”应该用于非指针属性。
  • 当属性是对象的指针时,需要使用“retain”。 @synthesize生成的setter将保留(添加保留计数)对象。完成后,您需要释放对象。
  • 当对象是可变的时,需要使用“copy”。如果需要对象此刻的值,并且您不希望该值反映其他拥有者对对象所做的任何更改,请使用此选项。因为您正在保留副本,所以完成后需要释放对象。

@Blamdarot - 我需要同时使用ARC来发布它吗? - Dejell
11
不需要。如果您在使用ARC时执行释放操作,我认为您会收到编译器错误。 - Blamdarot
55
“始终使用非原子操作”这条建议并不好。当你使用非原子操作时,你应该知道自己将失去什么。 - Jesse Rusak
8
同意。特别是许多人似乎不知道非原子值不会被getter保留或自动释放。非原子通常是适当的,但盲目模仿编程很少是合适的。 - Catfish_Man
当实现自己的属性访问器时(即不使用@synthesize,如果未为已声明的属性提供访问器,则现在是默认/隐含的),需要使用nonatomic标志。例如,如果您希望自定义视图在修改其backgroundColor属性时标记自身以进行显示,那么您必须声明属性为nonatomic,然后自己实现-setBackgroundColor:访问器方法(用于分配新颜色并调用-setNeedsDisplay:)。否则,最好不指定nonatomic标志将其默认为原子操作。 - Joshua Nozzi
11
建议保留默认的 atomic 与建议使用 nonatomic 一样糟糕。两种选择都不是“正确”的,因此语言设计者选择了更安全的解决方案。实际上,通常情况下使用nonatomic 是更好的选择,因为它省略了非常昂贵的线程锁。唯一需要使用 atomic 的原因是如果你的属性可能会在多个线程中设置(这种情况下省略它可能会导致过度释放或泄漏)。 - Adam Kaplan

162

阅读了许多文章后,我决定将所有属性信息整合在一起:

  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. 原子操作 -原子操作指只有一个线程可以访问该变量(静态类型)。 -原子操作是线程安全的。 -但它在性能上较慢。 -原子操作是默认行为。 -非垃圾回收环境中的原子访问器(即使用retain/release/autorelease)将使用锁来确保另一个线程不会干扰正确的设置/获取值。 -它实际上不是关键字。

示例:

@property (retain) NSString *name;

@synthesize name;
  1. nonatomic -非原子操作意味着多个线程可以访问变量(动态类型)。 -非原子操作是线程不安全的。 -但在性能方面很快。 -非原子操作不是默认行为,我们需要在属性属性中添加nonatomic关键字。 -当两个不同的进程(线程)同时访问相同的变量时,可能会导致意外行为。

示例:

@property (nonatomic, retain) NSString *name;

@synthesize name;

解释:

假设有一个名为“name”的原子字符串属性,如果您从线程A调用[self setName:“A”],从线程B调用[self setName:“B”],并从线程C调用[self name],则所有不同线程上的操作都将按顺序执行,这意味着如果一个线程正在执行setter或getter,则其他线程将等待。这使属性“name”读/写安全,但如果另一个线程D同时调用[name release],则此操作可能会导致崩溃,因为此处没有涉及setter/getter调用。这意味着对象是读/写安全的(原子性),但不是线程安全的,因为其他线程可以同时向对象发送任何类型的消息。开发人员应确保此类对象的线程安全。

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

  1. strong(iOS4 = retain) -它表示“在我不再指向它之前,请将其保留在堆中” 换句话说,“我是所有者,您不能在我同意之前dealloc此对象,与retain相同” -仅在需要保留对象时才使用strong。 -默认情况下,所有实例变量和局部变量都是强指针。 -我们通常为UIViewControllers(UI项的父级)使用strong。 -strong与ARC一起使用,基本上帮助您不必担心对象的保留计数。当您完成使用时,ARC会自动释放它。使用关键字strong意味着您拥有该对象。

示例:

@property (strong, nonatomic) ViewController *viewController;

@synthesize viewController;
weak(iOS4 = unsafe_unretained) -这意味着“只要有其他人强烈指向它,就将其保留” -与assign相同,没有retain或release -“弱”引用是一种不保留的引用。 -我们通常使用weak来处理IBOutlets(UIViewController的子对象)。这是因为子对象只需要在父对象存在时存在。 -弱引用是一种不保护所引用对象免受垃圾回收器回收的引用。 -Weak本质上是一个未保留的属性。除了当对象被释放时,弱指针会自动设置为nil。
@property (weak, nonatomic) IBOutlet UIButton *myButton;

@synthesize myButton;

强引用和弱引用解释,感谢BJ Homer:

想象一下我们的对象是一只狗,这只狗想要逃跑(被释放)。 强引用就像是狗的绳索。只要你把绳子系在狗身上,狗就不会逃跑。如果五个人把他们的绳子都系在一只狗身上(即一个对象有五个强引用),那么只有当这五个绳子都被解开时,狗才会逃跑。 而弱引用则像是指着狗说“看!一只狗!”的小孩。只要狗还在绳索上,小孩们就能看到狗,并且仍然会指着它。但是,只要所有的绳索都被解开了,无论有多少小孩在指着它,狗都会逃跑。 当最后一个强引用(绳索)不再指向一个对象时,该对象将被释放,所有的弱引用都将被清零。 何时使用弱引用? 唯一需要使用弱引用的时候是为了避免保留环(例如,父对象保留子对象,子对象也保留父对象,因此两者都永远不会被释放)。

  1. retain = strong -保留原有的值并分配新值。 -在赋值时,使用-retain指定新值应该被发送,并使用-release发送旧值。 -retain与strong相同。 -苹果公司表示,如果您编写了retain,则它将自动转换/像strong一样工作。 -像“alloc”这样的方法包括隐式的“retain”。
@property (nonatomic, retain) NSString *name;

@synthesize name;
  1. assign -assign 是默认的属性,它简单地执行变量赋值 -assign 是一个属性特性,它告诉编译器如何合成属性的setter实现 -我会将C语言原始属性设置为assign, 将Objective-C对象的弱引用属性设置为weak。

示例:

@property (nonatomic, assign) NSString *address;

@synthesize address;
  1. unsafe_unretained

    -unsafe_unretained是一个所有权限定符,用于告诉ARC如何插入retain/release调用 -unsafe_unretained是assign的ARC版本。

示例:

@property (nonatomic, unsafe_unretained) NSString *nickName;

@synthesize nickName;
  1. 拷贝 -当对象是可变的时,需要使用拷贝。 -拷贝指定新值在赋值时发送 -copy,旧值发送-release。 -拷贝类似于retain返回一个对象,你必须在非垃圾收集环境中显式释放它(例如,在dealloc中)。 -如果你使用了拷贝,那么仍然需要在dealloc中释放它。 -如果你需要对象的值就像此刻一样,并且不希望该值反映其他对象所有者所做的任何更改,则使用此选项。因为你正在保留拷贝,所以使用完对象后需要释放它。

示例:

@property (nonatomic, copy) NSArray *myArray;

@synthesize myArray;

2
我认为在ARC之后,不再使用retain。 - mert
1
完整列表缺少2个选项:setter和getter,它们也是唯一需要参数的选项。 - Scott Chu
强制或保留是仅适用于对象类型的默认值。它不能用于原始类型。 - Saleh Enam Shohag

11

原子属性只能被一个线程同时访问。它是线程安全的。默认是原子的。请注意,没有原子关键字。

非原子意味着多个线程可以访问该项。它是线程不安全的。

因此,在使用原子属性时应非常小心,因为它会影响您代码的性能。


4
注意:属性的原子性不等同于对象的线程安全性。 - jk7

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接