我是不是需要在块内的 strongSelf = weakSelf 分配上检查 nil ?

Posted

技术标签:

【中文标题】我是不是需要在块内的 strongSelf = weakSelf 分配上检查 nil ?【英文标题】:Do I need to check for nil on my strongSelf = weakSelf assignment inside a block?我是否需要在块内的 strongSelf = weakSelf 分配上检查 nil ? 【发布时间】:2015-08-16 23:46:21 【问题描述】:

例如,我正在使用 SVInfiniteScrolling (https://github.com/alexanderedge/SVInfiniteScrolling)。

我有一些看起来像这样的代码...

- (void)initializeInfiniteScrollingForTableView 
    __weak MyViewController *weakSelf = self;

    [self.tableView addInfiniteScrollingWithActionHandler:^
        MyViewController *strongSelf = weakSelf;
        if (!strongSelf.endReached) 
            [strongSelf fetchData];
        
        else 
            [strongSelf.tableView.infiniteScrollingView stopAnimating];
        
    ];

我想知道的是......我需要在这样使用之前检查 strongSelf 是否为零......

...
[self.tableView addInfiniteScrollingWithActionHandler:^
    MyViewController *strongSelf = weakSelf;
    if (strongSelf)  // <== ** Is this needed? **
        if (!strongSelf.endReached) 

这就是我问的原因。从这个链接 (http://www.apeth.com/iosBook/ch12.html#EXstrongWeakDance) 上的第 3 点开始,它说 “nil 测试是因为,在多线程情况下,我们对 self 的弱引用可能在上一步之前已经从我们下面消失了;然后它将是无,因为它是一个 ARC 弱引用,在这种情况下,继续没有意义。”

需要这个检查吗?我以为你第一次在块中使用了对 weakSelf 的引用,它会在表达式的持续时间内保留?

【问题讨论】:

@zaph 嗯。我正在努力思考这个问题。我对这个块没有强烈的参考吗?如果您查看包含 UIScrollView 的函数 addInfiniteScrollingWithActionHandler 的类别,它将块保存在视图中,然后将子视图添加到 UIScrollView(在本例中为我的 tableview)。这意味着我的 tableview 对块有很强的引用。我需要通过上面的弱强舞蹈来打破它。这不正确吗? @zaph 我希望你能帮助我理解这个过程。这个问题总是让我陷入困境。所以这个属性块就在这里(github.com/alexanderedge/SVInfiniteScrolling/blob/master/…),它在这里设置(github.com/alexanderedge/SVInfiniteScrolling/blob/master/…)。只要它存在,我的 self.tableView 就持有这个块,对吗?而且这个街区会强烈地引用我(没有舞蹈),对吧?我错过了什么? @zaph 所以我什至不需要将弱重新分配给强,因为这不是多线程情况,并且块在主线程上运行对吗?只是 weakSelf 本身会阻止这里的保留循环,我很好,对吗? @zaph 我不同意这个保留周期。此链接上的第二个代码块有一个很好的例子。 blackpixel.com/writing/2014/03/capturing-myself.html (来自链接:“如果块包含在另一个对象中,而该对象自身保持强引用”)。当 self 没有抓住块时,infiniteScrolling 的东西抓住了块,而 self 抓住了 infiniteScrolling 的东西。如链接所示,编译器不会注意到并警告您,但它仍会创建一个保留循环。 @zaph 接口文件中有指向self.tableView的强指针。它坚持住块。就像在最后一个链接中一样,没有指向块的指针,但是有一个指向这个 tableView 的指针,它保存了块。 【参考方案1】:

对于您发布的代码,无需检查nil

这行代码:

if (!strongSelf.endReached) 

如果 strongSelf 为 nil,将评估为 false。延伸阅读:Sending a message to nil?

然后这行代码就会执行:

[strongSelf.tableView.infiniteScrollingView stopAnimating];

如果 strongSelf 为 nil,该行将什么都不做(甚至不会出错)。

但是,一些开发人员认为无论如何检查 nil 是一种最佳做法,以防有人稍后添加代码并且它确实关心 nil。所以你应该考虑这样做:

MyViewController *strongSelf = weakSelf;
if (!strongSelf) 
  return;


... the rest of your code ...

【讨论】:

【参考方案2】:

我认为在你的行中:

if (!strongSelf.endReached) 

如果 self 为 nil,这将评估为 YES,这可能不是您想要的。

发生您的代码做了正确的事情(什么也不做),因为这两个子句都只使用 self 来调用方法,然后什么都不会发生。最终结果与您进行 nil-check 相同,但执行路径错误。如果有人添加不使用 self 的代码,这可能会在未来产生后果。

【讨论】:

【参考方案3】:

您的消息来源指的是weakSelf 可能已被释放,nil'd 在块执行之前可能已经开始了;因此,在strongSelf 获得强引用并保留它之前。 strongSelf 是为了确保weakSelf 引用的对象不是nil'd 块执行期间。

ARC 中没有“使用”变量的概念,因此在处理 __weak 变量时需要 retain 它 -- __weak 明确 选择退出。

话虽如此,在这种情况下严格来说不需要进行检查,因为发往nil 的消息始终是无操作的。如果您打算将strongSelf 插入到数组中,或者如果在消息之间释放它会使您的程序进入无效状态,那就另当别论了。

总而言之,当您需要保证__weak 变量在一段时间内不会是nil'd,请使用__strong,并在使用前检查它不是nil

【讨论】:

以上是关于我是不是需要在块内的 strongSelf = weakSelf 分配上检查 nil ?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 XSL-FO 中缩小块内的多个“外部图形”

内联块内的文本选择,中间没有空格

AFNetworking 未在块内同步返回数据

为啥实例变量在块内时似乎消失了?

Oracle在块内是如何组织和管理数据的

为啥在块外设置时,`scheduledTimer` 会正确触发,而不是在块内?