繁体   English   中英

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

[英]Do I need to use a weak self pointer if a method called from a Block uses self?

使用self. in块会导致保留周期,因此我需要创建对weakSelf的引用。 我明白了

但!

如果从我的块中调用了一个使用self的方法,是否还会引起保留周期?例​​如,如果我从一个块中重新加载UITableView ,并且在我的某些UITableView委托方法中将其称为self. ,是否导致了保留周期?这是否意味着我必须到处都绕过这个弱引用?

我可能会误解您的问题,但是您的措辞是:

如果从我的代码块中调用了一个使用“ self。”的方法,这是否还会导致保留周期? 例如,如果我从一个块中重新加载一个UITableView,并且在我的某些UITableView委托中将其称为“自我”,那是否会导致保留周期? 这意味着我必须到处绕过这个弱引用?

暗示您误解了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

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

不完全的。 当您通过引用实例变量直接或间接在块中使用self时,编译器将警告您可能会创建一个引用循环。 注意 :有其他使用或不使用块来创建参考周期的方法,编译器根本不会警告您。管理周期只是您需要注意的事情。)

如果将对块的引用存储在self引用的对象中,则实际上只会创建一个循环。 但是,这本身并不坏,只要您在某个时候手动中断循环(例如,通过将nil存储在引用该块的变量中),该循环根本就不会有问题。

最后...

本身无需担心:

我将UITableView委托称为“自我”。

因为该self只是该委托的局部参数,因此其初始值(在某些时候返回调用链)来自于您评估的weakSelf引用并确定其不是nil ,然后在其上调用方法。

高温超导

首先: self 不会引起保留周期。 这是一个城市传奇。 错误是显而易见的:单个引用永远不会导致循环。 的使用self一个块内引起保留周期,如果该块是直接或间接地称为由self通过属性,也为例子。

给你的问:

如果您在块内“调用”方法,则消息可能具有接收者self ,因此在块内您会使用self 出于这个原因,它被捕获和保留。

如果你真的有没有使用self由块内既没有使用self也不使用的属性self不必的使用self并且不拍摄,因此不会保留。 但是在这种情况下,您可以使用一个悬空指针或一个零引用。

您无需担心块执行时的引用-最终它完成了所要做的所有工作,所有这些引用都消失了。

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

因此,如果您将一个块作为属性存储在self中,则该块不应捕获self。 通过让它访问并捕获自身的薄弱副本很容易做到。 请记住,在执行块时,自身的弱副本可能为零。 这意味着自我对象已经离开了我们的世界,您的障碍物可能不需要执行任何操作。

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

长答案

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

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

看看Apple网站上的文档 它清楚地指出,值是在块中捕获的,而捕获对象引用则将该对象保留在块中。

基本上,这意味着引用一个块中的对象会将其keepCount递增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消息传递系统的实现方式是,调用方法不会保留此对象,该方法或该方法的实现,因为这是一个简单的函数调用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM