iOS阻止和对自我的强/弱引用

时间:2013-09-26 02:42:20

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

我有一个关于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;
        });
    });

非常感谢任何信息或解释!

3 个答案:

答案 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练习),然后使用您的最后一个示例,其中您有第二个弱指针。否则你可以使用你的另一个例子。

话虽如此,有几点意见:

  1. 首先必须使用此weakSelf模式,这不是一个明确的结论。有些人错误地认为他们使用这个weakSelf模式来避免强烈的参考周期(a.k.a. retain cycle)。但是这个代码示例并不构成强大的参考周期。它只是在调度代码执行时保留对象,这是一个非常不同的考虑因素。

    事实上,有时你需要/想要那个。有时候你没有。这取决于您正在解决的业务问题。当然,您经常不希望它强烈引用self,在这种情况下,weakSelf模式非常有意义。但情况并非总是如此。

    但我的观点是,你不应该追求这种weakSelf模式(至少在这个dispatch_async场景中)以避免强烈的参考周期。没有这样的循环。这是一个问题,你有一个块变量(例如一些completionHandler块)。在这种情况下,weakSelf模式至关重要。但不是在这里。

  2. 但是让我们考虑一下您不希望self保留的情况。然后是一个问题,你是否希望首先继续发送代码。如果没有,也许您应该使用具有可取消操作的操作队列而不是GCD。

    例如,我很惊讶在一些后台网络请求运行时,人们是否经常会想要保留视图控制器,但是不要担心他们是否应该取消后台网络请求。第一名。通常,后者是一个更重要的设计考虑因素(例如,您下载的PDF或图像占用的视频控制器将占用更多的系统资源(内存和网络带宽)。

  3. 但我们假设(a)您确实希望调度的代码继续执行,但(b)您不想保留self。 (这似乎是一种罕见的情况,但它是你所问过的那个,所以让我们继续这样做。)最后一个问题是你是否还需要你的strongSelf结构。在您的情况下,您只需调用self的单个方法,就不需要使用此strongSelf构造。只有当你要顺从ivars或者需要避免竞争条件时,这才是至关重要的。但是,在此示例中,假设发送给nil对象的消息不执行任何操作,您在技术上通常根本不需要担心此strongSelf构造。

  4. 别误会我的意思。围绕weakSelf模式,以及有时伴随它的嵌套strongSelf模式,这是一件好事。我只是建议理解何时真正需要这些模式。而且我认为GCD与可取消NSOperation的选择往往是一个更为关键但却经常被忽视的问题。

答案 2 :(得分:3)

创建块并将其存储在堆栈中。因此,当创建块的方法返回时,块将被销毁。

如果某个块成为实例变量,ARC会将该块从堆栈复制到堆中。您可以使用复制消息显式复制块。您的块现在是基于堆的块而不是基于堆栈的块。而且你必须处理一些内存管理问题。块本身将对它引用的任何对象保持强引用。在块外部声明__weak指针,然后在块内引用此指针以避免保留周期。