Swift的守卫关键词

Dav*_*bel 190 swift2 guard-statement

Swift 2引入了guard关键字,可用于确保准备好配置各种数据.我在这个网站上看到的一个例子演示了一个submitTapped函数:

func submitTapped() {
    guard username.text.characters.count > 0 else {
        return
    }

    print("All good")
}
Run Code Online (Sandbox Code Playgroud)

我想知道使用guard是否与使用if条件的旧式方式有所不同.它是否会带来好处,而使用简单的支票是无法获得的?

Jor*_*ego 361

阅读本文后,我注意到使用Guard的好处

在这里你可以比较使用guard和一个例子:

这是没有后卫的部分:

func fooBinding(x: Int?) {
    if let x = x where x > 0 {
        // Do stuff with x
        x.description
    }

    // Value requirements not met, do something
}
Run Code Online (Sandbox Code Playgroud)
  1. 在这里,您可以在所有条件下放置所需的代码

    你可能不会立即看到这个问题,但是你可以想象如果在运行你的语句之前嵌套了许多条件都需要满足它会变得多么混乱

清理它的方法是首先执行每个检查,如果不满足则退出.这样可以轻松了解使此功能退出的条件.

但现在我们可以使用后卫,我们可以看到可以解决一些问题:

func fooGuard(x: Int?) {
    guard let x = x where x > 0 else {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
    x.description
}
Run Code Online (Sandbox Code Playgroud)
  1. 检查你想要的条件,而不是你没有的条件.这再次类似于断言.如果不满足条件,则运行guard的else语句,该语句会中断该函数.
  2. 如果条件通过,则此处的可选变量将在调用guard语句的范围内自动解包 - 在本例中为fooGuard(_ :)函数.
  3. 您正在及早检查不良情况,使您的功能更易读,更易于维护

同样的模式也适用于非可选值:

func fooNonOptionalGood(x: Int) {
    guard x > 0 else {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
}

func fooNonOptionalBad(x: Int) {
    if x <= 0 {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
}
Run Code Online (Sandbox Code Playgroud)

如果您还有任何问题,可以阅读整篇文章:Swift guard声明.

包起来

最后,阅读和测试我发现,如果你使用后卫解开任何选项,

这些未包装的值会留在您的代码块的其余部分中使用

.

guard let unwrappedName = userName else {
    return
}

print("Your username is \(unwrappedName)")
Run Code Online (Sandbox Code Playgroud)

这里,展开的值仅在if块内可用

if let unwrappedName = userName {
    print("Your username is \(unwrappedName)")
} else {
    return
}

// this won't work – unwrappedName doesn't exist here!
print("Your username is \(unwrappedName)")
Run Code Online (Sandbox Code Playgroud)

  • @GeRyCh在一个guard语句中展开使得该变量可用于_after_ guard语句,而不是在其中.花了一些时间来习惯这个. (6认同)
  • 嘿@Eric你做了一个很棒的帖子!感谢您让它易于理解! (3认同)
  • 这是一个非常好的答案!好乔治 (2认同)
  • 我正在使用guard 来解开NSError。但是当我尝试在保护范围内使用它时(例如将错误传递给某个回调),它说“在保护条件中声明的变量在其主体中不可用”。它有任何意义吗?谢谢 (2认同)
  • 这是[另一篇优秀的文章](http://swiftalicio.us/2016/01/guard-guard/)关于使用guard来干净地展开选项.很好地总结了它. (2认同)

tak*_*shi 35

与之不同if,guard创建可以从其块外部访问的变量.解开很多Optionals 很有用.


Air*_*ity 23

真的有两大好处guard.一个是避免厄运的金字塔,正如其他人所提到的那样 - 许多讨厌的if let陈述彼此嵌套,越来越向右移动.

另一个好处往往是你想要实现的逻辑更" if not let"而不是" if let { } else".

这是一个例子:假设你想要实现accumulate- 它之间的交叉mapreduce它给你一个运行减少的数组.这是guard:

extension Sliceable where SubSlice.Generator.Element == Generator.Element {

    func accumulate(combine: (Generator.Element,Generator.Element)->Generator.Element) -> [Generator.Element] {
        // if there are no elements, I just want to bail out and
        // return an empty array
        guard var running = self.first else { return [] }

        // running will now be an unwrapped non-optional
        var result = [running]

        // dropFirst is safe because the collection
        // must have at least one element at this point
        for x in dropFirst(self) {
            running = combine(running, x)
            result.append(running)
        }
        return result
    }

}


let a = [1,2,3].accumulate(+)  // [1,3,6]
let b = [Int]().accumulate(+)  // []
Run Code Online (Sandbox Code Playgroud)

如何在没有后卫的情况下编写它,但仍然使用first它返回一个可选的?像这样的东西:

extension Sliceable where SubSlice.Generator.Element == Generator.Element {

    func accumulate(combine: (Generator.Element,Generator.Element)->Generator.Element) -> [Generator.Element] {

        if var running = self.first  {
            var result = [running]

            for x in dropFirst(self) {
                running = combine(running, x)
                result.append(running)
            }
            return result
        }
        else {
            return []
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

额外的嵌套是令人讨厌的,但是,拥有ifelse相隔甚远是不合逻辑的.对于空案例的早期退出,它更具可读性,然后继续使用函数的其余部分,就好像这不可能.


Dan*_*ieu 19

当满足条件时,使用guard它将guard块中声明的变量暴露给代码块的其余部分,将它们带入其范围.如前所述,对于嵌套if let语句肯定会派上用场.

请注意,guard需要在else语句中返回抛出.

用Guard解析JSON

下面是一个如何使用guard而不是if-let解析JSON对象的示例.这是博客条目的摘录,其中包含您可以在此处找到的游乐场文件:

如何在Swift 2中使用Guard来解析JSON

func parseJSONWithGuard(data : [String : AnyObject]) throws -> Developer {

    guard let firstname = data["First"] as? String  else {
        return Developer() // we could return a nil Developer()
    }

    guard let lastname = data["Last"] as? String else {
        throw ParseError.BadName // or we could throw a custom exception and handle the error
    }

    guard let website = data["WebSite"] as? String else {
        throw ParseError.BadName
    }

    guard let iosDev = data["iosDeveloper"] as? Bool else {
        throw ParseError.BadName
    }



    return Developer(first: firstname, last: lastname, site: website, ios: iosDev)

}
Run Code Online (Sandbox Code Playgroud)

下载游乐场:守卫游乐场

更多信息:

以下是The Swift Programming Language Guide的摘录:

如果满足guard语句的条件,则在guard语句的右括号后继续执行代码.使用可选绑定作为条件的一部分分配值的任何变量或常量都可用于guard语句出现的其余代码块.

如果不满足该条件,则执行else分支内的代码.该分支必须转移控制以退出该保护语句出现的代码块.它可以通过控制转移语句(如return,break或continue)执行此操作,或者它可以调用不返回的函数或方法,例如as fatalError().


zap*_*aph 7

一个好处是消除了许多嵌套if let语句.请参阅15:30左右的WWDC"Swift中的新内容"视频,标题为"末日金字塔".


Nar*_*a G 5

使用后卫我们的意图很明确.如果不满足特定条件,我们不想执行其余代码.这里我们也可以扩展链,请看下面的代码:

guard let value1 = number1, let value2 = number2 else { return }
 // do stuff here
Run Code Online (Sandbox Code Playgroud)


Hon*_*ney 5

什么时候使用警卫

如果你有一个带有一些UITextField元素或其他类型用户输入的视图控制器,你会立即注意到你必须打开textField.text可选项来获取里面的文本(如果有的话!).isEmpty对你没有任何好处,没有任何输入,文本字段只会返回nil.

因此,您可以将其中的一些解包并最终传递给将它们发布到服务器端点的函数.我们不希望服务器代码必须处理nil值或错误地将无效值发送到服务器,因此我们首先使用guard来打开这些输入值.

func submit() {
    guard let name = nameField.text else {
        show("No name to submit")
        return
    }

    guard let address = addressField.text else {
        show("No address to submit")
        return
    }

    guard let phone = phoneField.text else {
        show("No phone to submit")
        return
    }

    sendToServer(name, address: address, phone: phone)
}

func sendToServer(name: String, address: String, phone: String) {
  ...
}
Run Code Online (Sandbox Code Playgroud)

您会注意到我们的服务器通信函数将非可选的String值作为参数,因此警卫会事先解包.展开是有点不直观的,因为我们习惯于解包如果let解包在块内使用的值.这里的guard语句有一个关联的块,但它实际上是一个else块 - 也就是说,如果解包失败你会做的事情 - 这些值直接打开到与语句本身相同的上下文中.

// 关注点分离

没有警惕

如果不使用后卫,我们最终会得到一大堆类似于厄运金字塔的代码.这不适合在我们的表单中添加新字段或使代码非常易读.缩进可能很难遵循,特别是在每个分支上有如此多的else语句时.

func nonguardSubmit() {
    if let name = nameField.text {
        if let address = addressField.text {
            if let phone = phoneField.text {
                sendToServer(name, address: address, phone: phone)
            } else {
                show("no phone to submit")
            }
        } else {
            show("no address to submit")
        }
    } else {
        show("no name to submit")
    }
}
Run Code Online (Sandbox Code Playgroud)

是的,我们甚至可以将所有这些组合在一起,如果让语句分成用逗号分隔的单个语句,但是我们将无法找出哪个语句失败并向用户显示消息.

https://thatthinginswift.com/guard-statement-swift/


Naz*_*san 5

守卫声明要去做.它是几个不同的

1)允许我减少嵌套if语句
2)它增加了我的变量可访问的范围

如果声明

func doTatal(num1 : Int?, num2: Int?) {
  // nested if statement
    if let fistNum = num1 where num1 > 0 {
        if let lastNum = num2 where num2 < 50 {

          let total = fistNum + lastNum
        }
    }
 // don't allow me to access out of the scope 
 //total = fistNum + lastNum 
}
Run Code Online (Sandbox Code Playgroud)

警卫声明

func doTatal(num1 : Int?, num2: Int?) {
   //reduce  nested if statement and check positive way not negative way 
    guard let fistNum = num1 where num1 > 0 else{
      return
    }
    guard  let lastNum = num2 where num2 < 50 else {
     return
    }
    // increase my scope which my variable accessible
    let total = fistNum + lastNum

}
Run Code Online (Sandbox Code Playgroud)