Objective-C自动引用计数(ARC):strong vs retain和weak vs assign。

382
ARC引入了两个内存管理属性,分别是strongweak。除了明显不同的copy之外,请问strongretainweakassign之间有什么区别吗?根据我的理解,唯一的差别在于weak将指针分配为nil,而assign则不会这样做,这意味着一旦释放后向该指针发送消息就会导致程序崩溃。但如果我使用weak,这种情况永远不会发生,因为发送到nil的消息不会执行任何操作。关于strongretain我不知道是否有任何区别。在新项目中,是否应该使用assignretain,或已经被淘汰了?

12
ARC 引入了三个新的属性来管理属性的内存,分别是 strongweakunsafe_unretained。您可以使用这些属性来指定对象属性在被引用时如何保持和释放内存。 - NJones
5
@NJones,有两个属性特性(weakstrong)和4个变量生命周期限定符(__strong__weak__unsafe_unretained__autoreleasing)。请参见下面的ARC注释。 - Snowcrash
1
@SnowCrash 在某个版本的Xcode中,可能是开发者预览版,使用assign编译ARC时会出现错误。有许多已删除的答案涉及此问题。似乎在最终发布之前已经更改了这一点。对于我们这些早期采用者来说,unsafe_unretained是首选属性。要证明unsafe_unretained是有效属性,请查看苹果公司的“Objective-C编程”手册,在“封装数据”部分下的“对某些类使用不安全的未保留引用”小节中。其中写道:“对于属性,这意味着使用unsafe_unretained属性:”。 - NJones
8个回答

622

在阅读了许多文章、Stackoverflow帖子和演示应用程序以检查变量属性特性之后,我决定将所有属性信息整合在一起:

  1. atomic //默认值
  2. nonatomic
  3. strong=retain //默认值
  4. weak
  5. retain
  6. assign //默认值
  7. unsafe_unretained
  8. copy
  9. readonly
  10. readwrite //默认值

以下是详细的文章链接,您可以在其中找到上述所有属性,这肯定会对您有所帮助。非常感谢在这里给出最佳答案的所有人!!

iOS中的变量属性或修饰符

1.strong (iOS4 = retain )

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

例如:

@property (strong, nonatomic) ViewController *viewController;

@synthesize viewController;

2.弱引用 -

  • 它表示“只要有其他人强烈指向它,就保持此引用”。
  • 与assign相同,没有retain或release。
  • “弱引用”是一种您不保留的引用方式。
  • 我们通常在IBOutlets(UIViewController的子项)中使用弱引用。这样做的原因是子对象只需要存在于父对象存在的时间内。
  • 弱引用是一种不会阻止垃圾收集器回收所引用对象的引用。
  • 弱引用本质上与未保留的属性assign相同。但是当对象被释放时,弱指针会自动设置为nil。

例如:

@property (weak, nonatomic) IBOutlet UIButton *myButton;

@synthesize myButton;

强引用与弱引用的解释,感谢BJ Homer提供的解答

假设我们的对象是一只狗,并且这只狗想要逃跑(被释放)。

强引用就像是一条绳子拴在狗上面。只要你把绳子系在狗身上,狗就不会逃跑。如果有五个人把他们的绳子都系在同一只狗上(即五个强引用指向同一个对象),那么直到这五个绳子全部解除之前,狗都不会逃跑。

另一方面,弱引用就像是一些小孩子指着狗说“看,一只狗!”只要狗还被系在绳子上,小孩子们就可以看到狗,并且继续指着它。但是,一旦所有的绳子都被解除,无论有多少小孩子指着它,狗都会逃走。

当最后一个强引用(绳子)不再指向一个对象时,该对象将被销毁,所有的弱引用也将被清零。

我们何时使用弱引用?

唯一需要使用弱引用的情况是,如果你想避免循环引用(例如父对象保留子对象,而子对象也保留父对象,这样两个对象都不会被释放)。

3.retain = strong

  • 当进行赋值操作时,新值被发送retain消息,旧值被发送release消息,从而令新值被保留。
  • retain与strong的作用相同。
  • Apple表示,如果你写了retain,它会自动转换为/像是strong。
  • 诸如“alloc”之类的方法包含隐式的“retain”。

示例:

@property (nonatomic, retain) NSString *name;

@synthesize name;

4.assign

  • assign 是默认的,只是进行变量赋值。
  • assign 是一个属性特性,告诉编译器如何综合属性的setter实现。
  • 我会将 C 原始类型的属性使用 assign,而将 Objective-C 对象的弱引用使用 weak。

示例:

@property (nonatomic, assign) NSString *address;

@synthesize address;

7
弱引用是指不能保护所引用的对象免受垃圾回收器回收的引用。在 Objective-C 中并不存在垃圾回收器。 - bucherland
1
而这个层次结构是由iOS自动管理的。了解MVC的概念。我的意思是当ViewContorller被呈现时,iOS会将其视图层次结构加载到屏幕上(创建缺少的视图)。当另一个ViewController被呈现时,第一个视图层次结构将被释放。但是,如果您在ViewController中有“强引用”,则该视图无法在屏幕外被释放。这可能会对设备内存产生严重影响,并导致应用程序减速。(当然,设备拥有大量内存,在5-10个屏幕的应用中肯定很好,但是在庞大的应用中,您会遇到麻烦) - bucherland
1
我们在什么情况下使用weak?
  1. 对于UI对象,2. 代理,3. 块(应该使用weakSelf而不是self来避免内存循环(如上所述)
- bucherland
1
这个很好的答案中有一个错误 - strong - "ARC会在你使用完它后自动释放它",这是不正确的。 当没有指针指向弱对象时,ARC会自动释放它们。Strong - 是retain的同义词,因此我们需要将对象设置为nil来释放它。 - Ashwin G
2
@RDC,default 是什么意思?如果我使用 @property (nonatomic) NSString *string,它是 strong 吗?还是 assign?因为两者都是默认设置。 - Iulian Onofrei
显示剩余2条评论

236

来自过渡到ARC发布说明(属性特性部分的示例)。

// The following declaration is a synonym for: @property(retain) MyClass *myObject;

@property(strong) MyClass *myObject;

因此,在属性声明中,strongretain 是相同的。

对于 ARC 项目,我会使用 strong 而不是 retain,我会对 C 原始类型属性使用 assign,并对 Objective-C 对象的弱引用使用 weak


11
事实上,在 ARC 下,如果使用 assign 来声明一个对象类型的属性会导致编译错误。如果你不想对该属性进行 retain 操作,你需要使用 weakunsafe_unretained(显然是不安全的)来声明。 - cobbal
5
在部署目标为4.0的ARC项目中,我的assign编译得非常好。 - Pascal
9
@Pascal:在操作系统低于5.0的部署目标中,不允许使用弱引用。因此,对于旧项目,您仍然可以使用assign,但如果您升级到较新版本,则必须切换到weak。 - Mattia
3
是的,你的回答非常准确。我是在回应@Mattia时提出assign在某些情况下仍然有效的观点。 - Steven Oxley
1
@SatishMavani 因为当他们引入ARC时,希望人们不再局限于保留计数的思维模式,而是开始考虑对象图中的所有权关系。 - JeremyP
显示剩余4条评论

45

nonatomic/atomic

  • 使用nonatomic比使用atomic更快。
  • 除非您有非常特定的关于atomic的要求(这应该很少见),否则请始终使用nonatomic。(atomic无法保证线程安全 - 只是在同时被另一个线程设置时会阻塞对属性的访问)

strong/weak/assign

  • 使用strong来保留对象 - 虽然关键字retain是同义词,但最好使用strong。
  • 如果只想引用对象而不保留它,请使用weak - 用于避免保留周期(即委托) - 当释放对象时,将自动将指针置为空。
  • 对于原始值,请使用assign - 与weak完全相同,只是在释放对象时不会将其设为nil(默认设置)。

(可选)

copy

  • 用于创建对象的浅拷贝。
  • 将不可变属性设置为copy是一个好习惯 - 因为可变版本可以传递到不可变属性中,复制操作将确保始终处理不可变对象。
  • 如果传入不可变对象,则会保留它 - 如果传入可变对象,则会复制它。

readonly

  • 用于禁用属性的设置(如果有违规,将防止代码编译)。
  • 您可以通过直接更改其实例变量或在getter方法中更改来修改getter返回的内容。

@Sakthimuthiah是正确的,你必须纠正你的答案。 - Adela Toderici
@Sakthimuthiah是错误的(以及任何其他说它是的人)。Atomic并不能使其线程安全,尽管由于其行为可能会被轻易地误解。请阅读:https://dev59.com/Mmct5IYBdhLWcg3wHJzF - Chris J

39
据我所知,strongretain 是同义词,所以它们的作用是完全相同的
然后,weak 几乎像是 assign,但在所指向的对象被释放后自动设置为 nil。
这意味着,你可以简单地替换它们。然而,在我遇到的一个特殊情况下,我不得不使用assign而不是weak。假设我们有两个属性 delegateAssigndelegateWeak,都存储了拥有我们唯一强引用的委托。当委托被释放时,我们的 -dealloc 方法也会被调用。
// Our delegate is deallocating and there is no other strong ref.
- (void)dealloc {
    [delegateWeak doSomething];
    [delegateAssign doSomething];
}

代表对象已经在解除分配过程中,但仍未完全解除分配。问题在于weak引用已经被置为null了! 属性delegateWeak包含nil,但delegateAssign包含一个有效的对象(所有属性已释放和置null,但仍然有效)。

// Our delegate is deallocating and there is no other strong ref.
- (void)dealloc {
    [delegateWeak doSomething]; // Does nothing, already nil.
    [delegateAssign doSomething]; // Successful call.
}

这是一个非常特殊的情况,但它向我们展示了这些变量如何工作以及何时被置空。


https://dev59.com/UWox5IYBdhLWcg3wHAsA - Rukshan

21
Clang的Objective-C自动引用计数(ARC)文档清晰地解释了所有权限定符和修饰符:

有四个所有权限定符:

  • __autoreleasing
  • __strong
  • __*unsafe_unretained*
  • __weak

如果类型被限定为 __autoreleasing, __strong, 或者 __weak,则该类型是非平凡的所有权限定。

然后对于已声明属性,有六个所有权修饰符:

  • assign 意味着 __*不安全的非保留(unsafe_unretained)* 所有权。
  • copy 意味着 __强烈的(strong) 所有权,以及 setter 上拷贝语义的常规行为。
  • retain 意味着 __强烈的(strong) 所有权。
  • strong 意味着 __强烈的(strong) 所有权。
  • *unsafe_unretained* 意味着 __*不安全的非保留(unsafe_unretained)* 所有权。
  • weak 意味着 __弱引用(weak) 所有权。

除了 weak 外,这些修饰符在非 ARC 模式下可用。

从语义上讲,在五个管理操作:读取、赋值、初始化、销毁和移动中,所有权限定符具有不同的含义,在大多数情况下我们只关心赋值操作的差异。

在评估分配运算符时会发生分配。其语义因资格而异:
  • 对于__强引用对象,首先保留新指针;然后,使用基本语义加载lvalue;接下来,使用基本语义将新指针存储到lvalue中;最后释放旧指针。这不是原子操作;必须使用外部同步使其在并发加载和存储的情况下安全。
  • 对于__弱引用对象,更新lvalue以指向新指针,除非新指针是当前正在进行解分配的对象,在这种情况下,lvalue将更新为null指针。必须与对象的其他赋值、读取和新指针的最终释放同时执行这一操作。
  • 对于__*不安全未持有(unsafe_unretained)*对象,使用基本语义将新指针存储到lvalue中。
  • 对于__自动释放池对象,保留、自动释放,并使用基本语义将新指针存储到lvalue中。
请参考文档中的4.2节语义,了解阅读、初始化、销毁和移动方面的其他差异。

6
要理解强引用和弱引用,可以考虑以下示例。假设我们有一个名为displayLocalVariable的方法。
 -(void)displayLocalVariable
  {
     NSString myName = @"ABC";
     NSLog(@"My name is = %@", myName);
  }

在上面的方法中,myName变量的作用域仅限于displayLocalVariable方法,一旦该方法完成,持有字符串“ABC”的myName变量将从内存中被释放。

现在,如果我们想要在整个视图控制器生命周期内保留myName变量的值。为此,我们可以创建名为username的属性,它将对变量myName具有强引用(请参见下面代码中的self.username = myName;),如下所示:

@interface LoginViewController ()

@property(nonatomic,strong) NSString* username;
@property(nonatomic,weak) NSString* dummyName;

- (void)displayLocalVariable;

@end

@implementation LoginViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

}

-(void)viewWillAppear:(BOOL)animated
{
     [self displayLocalVariable];
}

- (void)displayLocalVariable
{
   NSString myName = @"ABC";
   NSLog(@"My name is = %@", myName);
   self.username = myName;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}


@end

在上面的代码中,你可以看到myName已经被分配给了self.username,并且self.username使用@property在接口中声明了一个强引用(间接地,它拥有对“ABC”字符串的强引用)。因此,在self.username存在的情况下,字符串myName将不会从内存中释放。

  • 弱引用

现在考虑将myName分配给一个弱引用dummyName,即self.dummyName = myName;与强引用不同,弱引用只会持有myName,直到myName有强引用为止。请参见下面的代码以了解弱引用:

-(void)displayLocalVariable
  {
     NSString myName = @"ABC";
     NSLog(@"My name is = %@", myName);
     self.dummyName = myName;
  }

在上述代码中,对于myName存在弱引用(即self.dummyName具有myName的弱引用),但是没有强引用指向myName,因此self.dummyName将无法持有myName的值。

现在再考虑下面的代码:

-(void)displayLocalVariable
      {
         NSString myName = @"ABC";
         NSLog(@"My name is = %@", myName);
         self.username = myName;
         self.dummyName = myName;
      } 

在上面的代码中,self.username对myName有一个强引用,因此即使方法结束后,self.dummyName仍将具有myName的值,因为myName与其关联有一个强引用。
现在,每当我们对变量进行强引用时,它的保留计数会增加1,并且只有在保留计数达到0时,变量才不会被释放。
希望这可以帮到您。

4

Strong:

  • 属性不会被销毁,只有当你将属性设置为nil时,对象才会被销毁。
  • 默认情况下,所有实例变量和局部变量都是强指针。
  • 只有在需要保留对象时才使用strong。
  • 通常我们用strong来管理UIViewControllers(UI项的父级)。
  • iOS 4(非ARC)可以使用Retain关键字。
  • iOS 5(ARC)可以使用Strong关键字。

示例: @property (strong, nonatomic) ViewController *viewController;

@synthesize viewController;

Weak

默认情况下自动获取并设置为nil。

  • 我们通常使用weak来管理IBOutlets(UIViewController的子项)和delegate。
  • 与assign相同,没有retain或release。

示例: @property (weak, nonatomic) IBOutlet UIButton *myButton;

@synthesize myButton;


2
strong和retain的区别:
  • 在iOS4中,strong等同于retain
  • 这意味着你拥有该对象并将其保存在堆中,直到不再指向它为止
  • 如果你写retain,它会自动像strong一样工作

weak和assign的区别:
  • “weak”引用是一种你不保留的引用,只要其他人强烈地指向它,你就可以保持它
  • 当对象被“释放”时,弱指针会自动设置为nil
  • "assign"属性属性告诉编译器如何合成属性的setter实现

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