【问题标题】:guard statement inconsistencies警卫声明不一致
【发布时间】:2016-05-26 19:53:10
【问题描述】:

// 在 guard 语句中先让 let 后跟 bool check 会导致编译错误

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

//先做bool检查,然后让工作

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

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

【问题讨论】:

    标签: swift swift2 guard-statement


    【解决方案1】:

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

    首先请注意,如果您的问题包含minimal, complete and verifiable example (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 语句与 w.r.t 并不完全相关(唯一)。问题的核心,因为我们看到 if/if let 语句的行为相同。下面的答案将使用guard 语句。


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

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

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

    修复它:将 , 替换为 where

    这在Language Guide - The Basics - Optional Binding中也有说明

    您可以在单个 if 语句中包含多个可选绑定 并使用where 子句检查Boolean 条件。如果可选绑定中的任何值是 nilwhere 子句 评估为false,考虑整个可选绑定 不成功。

    因此,如果我们想在guardif 语句中使用条件子句after 可选绑定,则需要使用where 子句来分隔条件子句来自前面的可选绑定。

    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 }
    }
    

    然而,这两者并不完全等价。

    • 语句 2. 上面的语句允许我们在初始条件子句变成false 的情况下短路guard 语句(然后不继续进行可选绑定,而是直接进入elseguard 声明)
    • 而 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 }
    }
    

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

    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(非可选总是成功的变量绑定/ assignment) 语句加上 where 关键字作为条件子句

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-13
      • 2017-04-30
      • 2021-10-09
      • 2021-01-29
      • 2017-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多