【问题标题】:iOS blocks and strong/weak references to selfiOS 块和对自身的强/弱引用
【发布时间】:2013-10-01 20:15:33
【问题描述】:

我有一个关于 iOS 块中对 self 的强引用和弱引用的问题。我知道在块内引用 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;
        });
    });

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

【问题讨论】:

  • 为什么要在块内创建对弱的强引用?

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


【解决方案1】:

你不需要做两组弱引用。使用块要避免的是保留循环——两个对象在不必要的情况下使彼此保持活动状态。

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

@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 并没有保持块处于活动状态。

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    两种结构都很好。这仅取决于您的意图。如果对象 (a) 在外部块开始之后但 (b) 在内部块在主队列上开始之前被释放,您希望发生什么?如果您不希望它在这种情况下保留(我可能猜想这是您的意图,因为您首先要完成这个 weakSelf 练习),然后使用您的最后一个示例,其中您有第二个弱指针.否则,您可以使用其他示例。

    话虽如此,但有几点意见:

    1. 首先你必须使用这个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 的选择通常是一个更为关键但经常被忽视的问题。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-12
        • 1970-01-01
        • 1970-01-01
        • 2013-09-08
        相关资源
        最近更新 更多