我有一个关于iOS中块的自我强弱引用的问题。我知道在块内引用self的正确方法是在块外创建一个弱引用,然后在块内强引用该弱引用,如下所示:
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
typeof(self) strongSelf = weakSelf;
NSLog(@"%@", strongSelf.someProperty);
});
但是,如果你有嵌套块会发生什么?一组参考文献足够吗?或者你需要为每个街区设置一套新套装?例如,以下哪项是正确的?
此:
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
typeof(self) strongSelf = weakSelf;
NSLog(@"%@", strongSelf.someProperty);
dispatch_async(dispatch_get_main_queue(), ^ {
strongSelf.view.frame = CGRectZero;
});
});
或者这个:
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
typeof(self) strongSelf = weakSelf;
NSLog(@"%@", strongSelf.someProperty);
__weak typeof(strongSelf) weakSelf1 = strongSelf;
dispatch_async(dispatch_get_main_queue(), ^ {
typeof(strongSelf) strongSelf1 = weakSelf1;
strongSelf1.view.frame = CGRectZero;
});
});
非常感谢任何信息或解释!
答案 0 :(得分:52)
您不需要制作两组弱引用。你想要用块来避免的是一个保留周期 - 两个对象让对方不必要地活着。
如果我有一个具有此属性的对象:
@property (strong) void(^completionBlock)(void);
我有这个方法:
- (void)doSomething
{
self.completionBlock = ^{
[self cleanUp];
};
[self doLongRunningTask];
}
当我将其存储在completionBlock
属性中时,该块将保持活动状态。但是因为它在块中引用了self
,所以块会保持self
活着直到它消失 - 但这不会发生,因为它们都是相互引用的。
在这种方法中:
- (void)doSomething
{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self cleanUp];
}];
[self doLongRunningTask];
}
您不需要对self
进行弱引用。该块将保持self
有效,因为它从内部引用self
,但由于我们所做的只是将块移到[NSOperationQueue mainQueue]
,self
不保持块活着。
希望这有帮助。
答案 1 :(得分:23)
两种结构都很好。这取决于你的意图。如果对象是(a)在外部块开始之后释放但是(b)在内部块在主队列上开始之前,你想要发生什么?如果您不希望它保留在这种情况下(我可能会猜测是您的意图,因为您首先要进行此weakSelf
练习),然后使用您的最后一个示例,其中您有第二个弱指针。否则你可以使用你的另一个例子。
话虽如此,有几点意见:
首先必须使用此weakSelf
模式,这不是一个明确的结论。有些人错误地认为他们有使用这个weakSelf
模式来避免强烈的参考周期(a.k.a. retain cycle)。但是这个代码示例并不构成强大的参考周期。它只是在调度代码执行时保留对象,这是一个非常不同的考虑因素。
事实上,有时你需要/想要那个。有时候你没有。这取决于您正在解决的业务问题。当然,您经常不希望它强烈引用self
,在这种情况下,weakSelf
模式非常有意义。但情况并非总是如此。
但我的观点是,你不应该追求这种weakSelf
模式(至少在这个dispatch_async
场景中)以避免强烈的参考周期。没有这样的循环。这是一个问题,你有一个块变量(例如一些completionHandler
块)。在这种情况下,weakSelf
模式至关重要。但不是在这里。
但是让我们考虑一下您不希望self
保留的情况。然后是一个问题,你是否希望首先继续发送代码。如果没有,也许您应该使用具有可取消操作的操作队列而不是GCD。
例如,我很惊讶在一些后台网络请求运行时,人们是否经常会想要保留视图控制器,但是不要担心他们是否应该取消后台网络请求。第一名。通常,后者是一个更重要的设计考虑因素(例如,您下载的PDF或图像占用的视频控制器将占用更多的系统资源(内存和网络带宽)。
但我们假设(a)您确实希望调度的代码继续执行,但(b)您不想保留self
。 (这似乎是一种罕见的情况,但它是你所问过的那个,所以让我们继续这样做。)最后一个问题是你是否还需要你的strongSelf
结构。在您的情况下,您只需调用self
的单个方法,就不需要使用此strongSelf
构造。只有当你要顺从ivars或者需要避免竞争条件时,这才是至关重要的。但是,在此示例中,假设发送给nil
对象的消息不执行任何操作,您在技术上通常根本不需要担心此strongSelf
构造。
别误会我的意思。围绕weakSelf
模式,以及有时伴随它的嵌套strongSelf
模式,这是一件好事。我只是建议理解何时真正需要这些模式。而且我认为GCD与可取消NSOperation
的选择往往是一个更为关键但却经常被忽视的问题。
答案 2 :(得分:3)
创建块并将其存储在堆栈中。因此,当创建块的方法返回时,块将被销毁。
如果某个块成为实例变量,ARC会将该块从堆栈复制到堆中。您可以使用复制消息显式复制块。您的块现在是基于堆的块而不是基于堆栈的块。而且你必须处理一些内存管理问题。块本身将对它引用的任何对象保持强引用。在块外部声明__weak指针,然后在块内引用此指针以避免保留周期。