3

// 在 guard 语句中执行 let first 后跟 bool check 会导致编译错误

   self.action = {  [weak self] in
      guard let `self` = self, data.isJSON() else { return }

// 先做布尔检查,然后让工作

  self.action = {  [weak self] in
     guard data.isJSON(), let `self` = self else { return }

上面的两个陈述似乎对我来说是等价的。为什么它在第一种情况下不起作用?

4

1 回答 1

4

将您的问题重新构建为最小、完整且可验证的示例

首先请注意,如果您的问题包含一个最小的、完整的和可验证的示例 (mvce),这将是更可取的,以目前的形式,它没有:

  • 关闭列表无关紧要,在这里只会令人困惑
  • 同样,未知的self(...没有上下文)只是令人困惑且与此问题无关

相反,您的问题的 mvce 可以按照以下方式构建:

func foo(bar: Int?) {
    // 1. why does this cause a compile time error?
    guard let baz = bar, true else { return }

    // 2. whereas this does not?
    guard true, let bax = bar else { return }
}

下面的答案将讨论这个 mvce,而不是原始问题中的模糊示例。最后还要注意,guard/guard let语句与问题的核心并不完全相关(唯一),因为我们看到if/if let语句的行为相同。下面的答案将使用guard语句。


现在,我们可以弥补上面 1. 中的编译时错误吗?而且,这两个陈述真的等价吗?

上面函数中第一条guard语句的编译时错误foo(...)很能说明问题

布尔条件需要where 将其与变量绑定分开

修复它:替换,where

这也在语言指南 - 基础 - 可选绑定中有所说明

您可以在单个if语句 中包含多个可选绑定,并使用where子句来检查Boolean条件。如果可选绑定中的任何值是nilwhere子句的计算结果为false,则整个可选绑定被认为是不成功的。

因此,如果我们想在or语句中的可选绑定之后使用条件子句,则需要使用子句将条件子句与前面的可选绑定分开。guardifwhere

func foo(bar: Int?) {
    // 1. ok, compiles
    guard let baz = bar where true else { return }

    /* 2. or, include a conditional-clause prior to the
          optional binding, but is this really equivalent..? */
    guard true, let bax = bar else { return }
}

然而,这两者并不真正等同。

  • guard语句 2. 上面的语句允许我们在初始条件子句被证明是的情况下短路语句false(然后不进行可选绑定,而是直接进入语句else块)guard
  • 而上面的 1. 允许相反:仅当可选绑定成功时才检查条件子句。

在某些情况下,我们会更喜欢其中一个,例如,如果条件子句包含一些非常繁重的计算,我们可能不想执行这些计算,除非我们确定可选绑定成功

let heavyStuff: () -> Bool = { print("foo"); /* ... */ return true }

func foo(bar: Int?) {
    /* 1. call the heavyStuff boolean construct only if
          the optional binding succeeds */
    guard let baz = bar where heavyStuff() else { return }

    /* 2. possibly unnesessarily perform heavy boolean
          stuff prior to failing the optional binding */
    guard heavyStuff(), let bax = bar else { return }
}

一个不太人为的例子是,如果我们想在后面的条件子句中使用成功绑定的变量 in(这里:作为参数)

let integerStuff: (Int) -> Bool = { _ in /* ... */ return true }

func foo(bar: Int?) {
    /* 1. call the integerStuff boolean construct only if
          the optional binding succeeds, using the binded
          immutable as closure argument */
    guard let baz = bar where integerStuff(baz) else { return }

    /* 2. ... not really any good alternatives for such 
          flow if using this alternative */
    guard integerStuff(baz ?? 0), let bax = bar else { return }
}

最后请注意,从技术上讲,如果您真的想将初始可选绑定与以下条件子句分开,您可以使用虚拟case let(非可选总是成功的变量绑定/赋值)语句加上where条件子句的关键字

let checkThis: () -> Bool = { /* ... */ return true }

func foo(bar: Int?) {
    // ...
    guard let baz = bar, case let _ = () where checkThis() else { return }
}

然而,这只是为了展示这种技术性;在实践中,只需使用一个where子句。

于 2016-05-27T13:01:17.280 回答