繁体   English   中英

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

[英]iOS blocks and strong/weak references to self

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

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

您不需要制作两组弱引用。 你想要用块来避免的是一个保留周期 - 两个对象让对方不必要地活着。

如果我有一个具有此属性的对象:

@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不会使块保持活动状态。

希望这可以帮助。

两种结构都很好。 这取决于你的意图。 如果对象是(a)在外部块开始之后释放但是(b)在内部块在主队列上开始之前,你想要发生什么? 如果您不希望它保留在这种情况下(我可能会猜测是您的意图,假设您首先进行了这个weakSelf ),那么请使用您的最后一个示例,其中您有第二个弱指针。 否则你可以使用你的另一个例子。

话虽如此,有几点意见:

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

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

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

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

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

  3. 但是我们假设(a)你真的希望调度的代码继续执行,但是(b)你不想保留self (这似乎是一个罕见的场景,但它是你所问过的那个,所以让我们继续追求。)最后一个问题是你是否需要你的strongSelf构造。 在你的情况下,你只需要调用一个self方法,你不需要打扰这个strongSelf结构。 只有当你要顺从ivars或者需要避免竞争条件时,这才是至关重要的。 但是,在这个例子中,假设发送给nil对象的消息什么都不做,你在技术上通常根本不需要担心这个strongSelf构造。

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

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

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

暂无
暂无

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

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