2

在块中使用self.会导致保留周期,因此我需要创建对weakSelf. 我明白这一点

但!

如果从我的块中调用一个使用self“的方法,这是否也会导致保留周期?例​​如,如果我UITableView从块中重新加载 a 并且在UITableView我调用的某些委托方法中self.,我是否会导致保留周期?这是否意味着我必须到处传递这个弱引用吗?看起来很做作。

4

4 回答 4

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持有一个对 object 的强引用,例如在实例变量或属性中,那么只要它还活着B,那么至少B会保持活动状态(可能存在对 的其他强引用)。BA

如果一个对象A持有一个强引用BB持有一个,A那么你就有一个强引用循环——每个对象都让另一个对象保持活动状态,并且两者都不会被收集。这可能导致内存泄漏 - 从未收集过未使用的内存 - 除非两者AB从创建到程序结束都存在。

此外,您不会简单地通过将引用存储在方法的局部变量和参数中来创建强引用循环。就其性质而言,这些变量以及它们的内容是瞬态的,并在方法返回时被销毁。

self在块中使用

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

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

如果您将块的引用存储在由self. 然而,这本身并不坏,只要在某些时候您手动中断循环 - 例如通过存储nil在引用块的变量中 - 循环根本不会有问题。

最后...

本身没有什么可担心的:

UITableView 代表我称之为“自我”。

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

高温高压

于 2014-12-19T22:01:23.730 回答
3

首先:self不会导致保留周期。这是一个都市传说。错误很明显:单个引用永远不会导致循环。如果块也直接或间接地由 引用,例如通过属性self,则块内部的使用会导致保留循环。self

对你的问:

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

如果你真的没有使用self块内部,既不使用也不使用你self没有使用的属性,并且它不会被捕获,因此不会被保留。但在这种情况下,您可以有一个悬空指针或一个 nil'd 引用。selfself

于 2014-12-19T16:53:40.477 回答
2

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

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

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

于 2014-12-19T17:01:11.047 回答
1

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

长答案

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

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

查看Apple 网站上的文档。它明确指出值是在块中捕获的,并且捕获对象引用会将该对象保留在块中。

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

于 2014-12-19T17:11:55.723 回答