如果从Block调用的方法使用self,我是否需要使用弱自指针?

时间:2014-12-19 16:38:12

标签: ios objective-c objective-c-blocks retain-cycle

在块中使用self.会导致保留周期,因此我需要创建对weakSelf的引用。我理解这个

BUT!

如果从我的块中调用一个使用self"的方法,这是否也会导致保留周期?例如,如果我从块中重新加载UITableView并在我的UITableView委托方法中调用self.,我是否会导致保留周期?这是否意味着我必须绕过这个弱参考?似乎很好。

4 个答案:

答案 0 :(得分:5)

我可能会误读你的问题,但你的措辞是:

  

如果从我的块中我调用了一个使用" self的方法。",这是否也会导致保留周期?例如,如果我从一个块重新加载一个UITableView,而在我的一些UITableView代理中,我会调用" self。",我导致一个保留周期?这意味着我必须到处传递这个弱参考?

表明你误解了self是什么。希望如果是这样,以下内容将有所帮助而不会妨碍您的理解...

什么是self

标识符self只是方法的一个参数的名称,它只是隐式传递而不是像其他参数一样显式传递。例如,如果您有一个类:

@implementation MyClass

- (void) someMethod:(NSInteger)value
{
   ... self ... value
}

@end

然后该方法有效地(即为了清晰起见而略微弯曲事实):

- (void) someMethod:(NSInteger)value withObject:(MyClass *)self
{
   ... self ... value
}

当调用实例方法时,为self参数传递的值是对该方法应该操作的实例的引用,例如,电话

MyClass *someInstance = ...

[someInstance someMethod:42];

有效致电:

someMethod:42 withObject:someInstance

强参考周期

只要存在对该对象的强引用,对象(包括类块的实例)就会保持活动状态。

如果对象A拥有强引用,例如在实例变量或属性中,对象B,然后B将保持活动至少(可能存在对B的其他强引用)只要A还活着。

如果某个对象A拥有对B的强引用,并且B拥有一个A,则您有一个强引用周期 - 每个物体都保持另一个物体存活,并且都不会被收集。这个可能导致内存泄漏 - 永远不会收集未使用的内存 - 除非AB都意味着从创建到程序结束。

此外,只是通过将引用存储在方法的局部变量和参数中来创建一个强引用循环。就其本质而言,这些变量及其内容是瞬态的,并在方法返回时被破坏。

在块中使用self

  

使用" self。"在块中会导致保留周期,因此我需要创建对weakSelf的引用。

不完全。当您通过引用实例变量直接或间接在块中使用self时,编译器将警告您可能创建引用循环。 (注意:还有其他方法可以创建引用周期,包括使用和不使用块,编译器根本不会警告您。管理周期只是您需要注意的事项。)< / p>

如果在self引用的对象中存储对块的引用,则实际上只会创建一个循环。然而,这本身就是坏的,只要在某些时候你手动打破循环 - 比如将nil存储在引用该块的变量中 - 循环不一定有问题。

<强>最后...

您无需担心本身

  

UITableView委托我打电话给#34; self。&#34;

因为self只是委托的本地参数,其初始值(在某些时候回到调用链上)来自您评估weakSelf引用并确定它不是nil {1}}然后在其上调用方法。

HTH

答案 1 :(得分:3)

首先:self NOT 导致保留周期。这是一个都市传奇。不正确是显而易见的:单个引用永远不会导致循环。如果块由self直接或间接引用,则块内self的使用会导致保留周期,例如通过属性。

致你的问:

如果你&#34;打电话&#34;在块内部的方法中,消息可能具有接收器self,因此您在块内使用self。对于原因,它会被捕获并保留。

如果您在使用self或使用self属性时未真正使用self,则表示您没有self的使用权未被捕获,因此不予保留。但在这种情况下,你可以有一个悬空指针或一个零参考。

答案 2 :(得分:2)

当块执行时你不需要担心引用 - 最终它完成了它做的任何事情,并且所有这些引用都消失了。

您需要担心的是创建块时捕获的引用。这些引用一直持续到块消失为止。因此,如果您的块具有对“self”的引用,那么该引用就是因为该块存在。如果你将该块存储在self属性中,那么你就有了一个循环。

因此,如果您将块存储为self中的属性,则块不应捕获self。通过让它访问并捕获自我的弱副本,可以轻松完成。请记住,当块执行时,self的弱副本可能为零。这意味着自我对象已经离开了我们的世界,你的块可能不需要做任何事情。

答案 3 :(得分:1)

简短回答:不,在这种情况下,自我不会被保留。

答案很长

首先,保留自我和参考周期并不是一回事。参考周期是许多对象之间强引用的循环:A-> B-> C-> A是保留周期。

一般的想法是,您希望始终保证,如果您在块中引用self,则不会强烈引用此块,也不要通过一系列强引用引用它。实际上,如果您确定在某些条件下打破保留周期,则可以有目的地使用保留周期。不是我个人推荐这个。

看看documentation on Apple's website。它清楚地表明值是以块的形式捕获的,捕获对象引用会在块中保留此对象。

基本上这意味着引用块中的对象会使其retainCount增加1,并且当此块被取消分配时,retainCount将减1。

但是,在块中使用__weak指针时,保留计数不受影响。

以下是一个例子:

- (void) doSomething {
   NSLog(@"%@", self);
}

- (void) callBlock {
   __weak typeof(self) weakSelf = self;
   dispatch_block_t block = ^{
      [weakSelf doSomething];
   };
}

当你写[obj method:params]时,这实际上会转换为以下调用: objc_msgSend(obj, @selector(method:), params)。 Objective-C的一个特性是,如果你在nil指针上调用一个方法,它将返回nil。 objc_msgSend(nil, @selector(anyselector), ...)始终返回nil这一事实保证了这一点。请注意,SEL只是一个const char [],因此它不会以任何方式影响保留计数。

因此,当块被执行时,如果你的对象被释放,弱weakSelf变量将被取消,并且块的主体将转换为objc_msgSending为零,除了浪费很少的CPU周期之外什么都不做。

总而言之,Objective-C消息传递系统的实现方式是调用方法不会保留此对象或此方法或此方法的实现,因为它是一个简单的函数调用。