可选链接与可选绑定:何时使用哪个?

时间:2016-08-21 16:07:12

标签: ios swift cocoa-touch

在搜索有用的方式来刷新UINavigationBar时(请参阅:How to properly refresh a UINavigationBar?),我遇到了这个问题:

这会按预期刷新UINavigationBar,即使不是一个干净的方式:

if let navigationController = self.navigationController {
    navigationController.popViewControllerAnimated(false)
    navigationController.pushViewController(self, animated: false)
}

但是,这会弹出当前视图控制器,但不会推送它:

navigationController?.popViewControllerAnimated(false)
navigationController?.pushViewController(self, animated: false)

这种行为如何解释?

2 个答案:

答案 0 :(得分:3)

可选绑定允许每次以相同的方式发生整个逻辑块。多行可选链接可能使其不清楚到底会发生什么,并且可能会受到竞争条件的影响而导致意外行为。

使用可选绑定,您将创建对刚刚解开的任何内容的新引用,通常,这是一个强大的参考。它也是一个本地参考,不会受到其他情况的突变。习惯上通常也是let。因此它将在其整个生命周期内保持相同的价值。

考虑你的例子......但稍微调整一下。

if let navigationController = self.navigationController {

    // this should always pass
    assert(navigationController === self.navigationController)

    navigationController.popViewControllerAnimated(false)

    // this next assert may fail if `self` refers to the top VC on the nav stack
    assert(navigationController === self.navigationController)

    // we now add self back onto the nav stack
    navigationController.pushViewController(self, animated: false)

    // this should also always pass:
    assert(navigationController === self.navigationController)
}

这种情况正在发生,因为当导航控制器将新的视图控制器添加到其导航堆栈(使用pushViewController)时,它会将传入的视图控制器的navigationController属性设置为等于它自己。并且当该视图控制器时从堆栈中弹出,它将该属性设置回nil

让我们看看可选链接方法,再次抛出这些断言:

let navController = navigationController

// this should always pass
assert(navController === navigationController)

// assuming navigationController does not evaluate to nil here
navigationController?.popViewControllerAnimated(false)

// this may fail if `self` is top VC on nav stack
assert(navController === navigationController)

// if above failed, it's because navigation controller will now evaluate to nil
// if so, then the following optional chain will fail and nothing will happen
navigationController?.pushViewController(self, animated: false)

// if the previous assert fail, then the previous push wasn't called
// and if the previous push wasn't called, then navigation controller is still nil
// and therefore this assert would also fail:
assert(navController === navigationController)

所以在这里,self上的实例属性被我们自己的方法调用设置为nil,导致可选绑定和放大器之间的行为差​​异。链接。

但即使我们没有通过我们自己在当前线程上的操作来改变该属性,我们仍然会遇到突然该属性正在评估nil的问题,即使它有一个有效的值。上一行。我们可以使用多线程代码来处理这个问题,其中另一个线程可能导致我们的属性值现在评估为nil,反之亦然。

简而言之,如果您需要确保全部或全部,则需要选择可选绑定。

答案 1 :(得分:2)

可选绑定将您绑定的内容存储在变量中。在这种情况下,它是navigationController

另一方面,可选链接不会将左侧的值放入变量中。它只说

  

我会检查问号左边的这个值是否为零。如果不是,请评估表达式的其余部分。如果是,则评估为nil

因此,在弹出视图控制器(第一行)后,self.navigationController变为nil,因此不会评估第二行中的方法调用。

您可能会遇到以下困惑:

  

为什么self.navigationController变为零?

一种可能性是selfnavigationController的顶级视图控制器。弹出self后,它不属于任何导航控制器。

  

为什么可选绑定工作但不是可选链接?

正如我之前所说,可选绑定将self.navigationController的值放入变量navigationController。因此,即使self.navigationController为零,navigationController仍保持其值。