ARC __block和__weak

时间:2013-07-23 14:11:18

标签: ios objective-c automatic-ref-counting

假设我正在尝试从块中访问self

[someObject successBlock:^(NSArray *result) {
    [self someSuccessMethod];
} failure:^(NSString *errorMessage, int status) {
    [self someFailureMethod];
}];

我了解这会创建一个保留周期,someObjectself永远不会被取消分配。

令我感到困惑的是,有__block个关键字实际发生了什么。我可以通过__weak对自我的引用来修复保留周期:

__weak MyClass* me = self;
[someObject successBlock:^(NSArray *result) {
    [me someSuccessMethod];
} failure:^(NSString *errorMessage, int status) {
    [me someFailureMethod];
}];

我不需要在这里使用__block,因为我不是要在块中修改me。根据我的理解,如果我不使用__block,则会在块内引用me的副本。我的问题是:如果块中引用的内容只是对象的副本,为什么原始代码块会创建保留周期?我猜想self的引用只是一个副本,因为我从不使用__block关键字。我错误地想到了这个吗?

4 个答案:

答案 0 :(得分:7)

在第一种情况下,该块会捕获self,即它会将self的副本保存为另一个指针。这会增加指向对象的保留计数,并导致保留周期。

在第二种情况下,该块会捕获me,即它会将me的副本保存为另一个 指针。这不会增加保留计数,因此不会导致保留周期。

(如果您在块外部和内部打印me地址,您会看到 地址不同。该块有自己的指向对象的弱指针。)

如果指向的对象被释放,则所有弱引用(包括由...保存的引用) 该块由Objective-C运行时设置为nil

(我只希望我做对了。)

答案 1 :(得分:4)

当两个对象彼此存储强引用时,会发生保留周期。最简单的情况是对象a存储对象b的强引用,而b执行相反的[1]。保留周期是Objective-C中的一个问题,因为它们使ARC相信这些对象总是在使用,即使这些对象没有被其他任何地方引用。

让我们回顾一些例子。您有对象z,它分配ab,使用它们,然后处理它们。如果ab首先在自己之间创建了保留周期,则ab将不会被取消分配。如果你多次这样做,你会严重泄漏记忆。

保留周期的另一个真实示例是a分配并强烈引用b对象,但您还存储了从ba的强引用(对象图中的许多较小的对象可能需要访问他们的父母)。

在这些情况下,最常用的解决方案是确保包含的对象只包含对其包含对象的弱引用,并确保兄弟对象不包含对彼此的强引用。

另一种解决方案(通常不那么优雅,但在某些情况下可能是合适的)可能在cleanup中使用某种自定义a方法,而忽略了对b的引用。因此,b会在调用cleanup时解除分配(如果b未在其他地方强引用)。这很麻烦,因为你不能从a的{​​{1}}执行此操作(如果有保留周期则永远不会调用它),并且因为你必须记得在适当的时候调用dealloc

  1. 请注意,保留周期也是可传递的(例如,对象cleanup强烈引用a,强烈引用强引用b的{​​{1}}。

  2. 所有这些都说:块的内存管理很难理解。

    您的第一个示例可以创建临时保留周期(仅当您的c对象存储对a的强引用时)。当块完成执行并被解除分配时,此临时保留周期消失。

    在执行过程中,self会将对someObjectself的引用存储到someObject,再将someObject存储到block。但同样,它只是暂时的,因为块不是永久存储在任何地方(除非block实现这样做,但这对于完成块来说并不常见。)

    因此,在第一个示例中,保留周期不是问题。

    通常,如果某个对象存储块而不是直接执行它,则块内的保留周期只是一个问题。然后很容易看出self强烈引用[someObject successBlock:failure:]self强烈引用block。请注意,从块内部访问任何 ivar 会自动生成对该块中block的强引用。

    相当于确保包含的对象不强引用其容器正在使用self来访问方法和ivars(如果通过访问器访问ivars,则使用属性时更好)。您的块对self的引用将很弱(它是不是副本,它是弱引用),这将允许__weak SelfClass *weakSelf = self取消分配它不再被强烈引用。

    可以说,为了以防万一,总是在所有块中使用self,存储与否,这是一种好习惯。我想知道为什么Apple没有将此作为默认行为。这样做通常不会对块代码做任何有害的事情,即使实际上不需要。


    self很少用于指向对象的变量,因为Objective-C不会强制执行此类对象的不变性。

    如果你有一个指向对象的指针,你可以调用它的方法,这些方法可以修改它,有或没有weakSelf__block对基本类型的变量(int,float等)更有用(仅?)。有关将__block与对象指针变量一起使用时会发生什么情况,请参阅here。您还可以阅读Apple的Blocks Programming Topics中有关__block的更多信息。

    编辑:修复了有关对象指针__block使用的错误。感谢@KevinDiTraglia指出它。

答案 2 :(得分:3)

您的第一个示例不会创建从不结束保留周期。可以保留周期,但是一旦块完成,将删除someObject的块的引用。所以someObject将至少存在,直到块完成。 这种临时保留周期可能是好事还是坏事,具体取决于您的需求:

如果你需要someObject至少活着,直到它的块完成,它没关系。但是,如果没有理由保留该对象,则应使用“弱”'来实现它。引用。

EG。 myObject是一个视图控制器,在这些块中从网络中获取图片。如果您从导航控制器弹出someObject,控制器在获取后无法显示图片,因此无需保留。成功或错误是无关紧要的,用户不再对someObject应该获取的图片感兴趣。在这种情况下,使用weak是更好的选择,但是块中的代码应该比self可能为零。

答案 3 :(得分:0)

你可以将self作为block的参数路径,正确地给出变量名'self',这将防止在块中自我保留。

你对'someObject和self永远不会被取消分配'是错的:当释放块时,self将被释放。块将被someObject释放。当SomeObject没有更多引用时,它将被释放。因此,如果你的自我对象拥有someObject,只需在你不再需要它时释放someObject。