假设我试图从一个块中访问self:
[someObject successBlock:^(NSArray *result) {
[self someSuccessMethod];
} failure:^(NSString *errorMessage, int status) {
[self someFailureMethod];
}];我理解这会造成一个保留周期,而且someObject和self永远不会被取消分配。
让我困惑的是使用/不使用__block关键字时实际发生了什么。我可以通过对self进行__weak引用来修复保留周期:
__weak MyClass* me = self;
[someObject successBlock:^(NSArray *result) {
[me someSuccessMethod];
} failure:^(NSString *errorMessage, int status) {
[me someFailureMethod];
}];这里不需要使用__block,因为我并不试图从块中修改me。据我所知,如果不使用__block,则在块中引用me的副本。我的问题是:如果在块中引用的只是对象的一个副本,那么为什么原始代码块创建保留周期?我猜想对self的引用只是一个副本,因为我从不使用__block关键字。我是不是想错了?
发布于 2013-07-23 15:15:11
在第一种情况下,块捕获self,即将self的副本保存为另一个强指针。这会增加指向对象的保留计数,并导致保留周期。
在第二种情况下,块捕获me,即将me的副本保存为另一个弱指针。这不会增加保留计数,因此不会导致保留周期。
(如果在块外部和内部打印me地址,您将看到地址是不同的。块有自己的指向对象的弱指针。)
如果将指向对象解除分配,则所有弱引用(包括块保存的引用)都由object运行时设置为nil。
(我只希望我做得对。)
发布于 2013-07-23 16:04:07
当两个对象彼此存储一个强引用时,就会发生保留循环。最简单的例子是对象a存储了对对象b和b的强引用,相反的1。保留循环是目标C中的一个问题,因为它们使b相信这些对象总是在使用中,即使这些对象不是从其他任何地方引用的。
让我们回顾一下一些例子。您有对象z,它分配a和b,利用它们,然后对它们进行处理。如果a和b首先在它们之间创建了一个保留循环,那么a和b就不会被取消分配。如果你这样做了几次,你就会严重地漏掉记忆。
保留周期的另一个实际示例是,如果a分配并强烈引用一个b对象,那么您还可以存储从b到a的强引用(对象图中的许多较小的对象可能需要访问它们的父对象)。
在这些情况下,最常见的解决方案是确保包含的对象只具有对其包含对象的弱引用,并确保兄弟对象不包含对彼此的强引用。
另一种解决方案(通常不那么优雅,但在某些情况下可能合适)可能是在a中有某种自定义的a方法,nils引用b。因此,当调用b时(如果b在其他地方没有被强烈引用),cleanup将被释放。这很麻烦,因为您不能从a的dealloc (如果有保留循环的话,就永远不会调用它),而且您必须记住在适当的时候调用cleanup。
a强烈引用b,后者强烈引用c,后者强烈引用a)。所有这些都说明:块的内存管理是很难理解的。
您的第一个示例可以创建一个临时保留周期(并且只有当self对象存储对someObject的强引用时)。当块完成执行并被解除分配时,这个临时保留周期就会消失。
在执行过程中,self将存储对someObject的引用、对block的someObject以及对self的block。但是,它只是暂时的,因为块不会永久地存储在任何地方(除非[someObject successBlock:failure:]实现这样做,但对于完成块来说并不频繁)。
因此,在您的第一个示例中,保留周期不是一个问题。
通常,只有当某个对象存储块而不是直接执行该块时,块中的保留循环才是一个问题。然后很容易看出self强烈引用了block,而block对self有很强的引用。请注意,从块内部访问任何ivar都会自动生成对该块中的self的强引用。
确保包含的对象不强烈引用其容器的等效方法是使用__weak SelfClass *weakSelf = self访问方法和ivars (如果您通过访问器访问ivars,就像使用属性时一样)。您的块对self的引用将是弱的(它是而不是副本,它是弱引用),这将允许self在不再被强引用时解除分配。
可以说,在所有块中始终使用weakSelf是很好的做法,无论存储还是不存储,以防万一。我不知道为什么苹果没有将此作为默认行为。这样做通常不会对块代码造成任何损害,即使实际上是不必要的。
__block很少用在指向对象的变量上,因为objects并不强制这样的对象不可变。
如果有指向对象的指针,则可以调用其方法,这些方法可以使用或不使用__block对其进行修改。__block更多(仅限?)适用于基本类型的变量(int、float等)。有关在对象指针变量中使用这里时发生的情况,请参见__block。你也可以通过苹果在块编程主题上读到更多关于块编程主题的信息。
编辑:修正了在对象指针上使用__block的错误。感谢@KevinDiTraglia的指点。
发布于 2014-02-16 15:29:27
您的第一个示例将不会创建一个永不终止的保留循环。有保留循环,好的,但一旦完成块,将删除从块到someObject的引用。因此,someObject将至少在区块完成之前存活。这样的临时保留周期可能是好的,也可能是坏的,这取决于您想要什么:
如果你需要你的someObject活着,至少直到它的块完成,这是可以的。但是,如果没有理由保留该对象,则应该使用“弱”引用来实现它。
例如:myObject是一个视图控制器,在这些块中从网络中获取图片。如果弹出导航控制器中的someObject,则该控制器在获取图片后将无法显示它,因此不需要保留它。无论是成功还是错误都无关紧要,用户对someObject应该获取的图片不再感兴趣。在这种情况下,弱的使用是更好的选择,但是块中的代码应该比self可能是零。
https://stackoverflow.com/questions/17812672
复制相似问题