Swift 2引入了guard关键字,它可以用来确保各种数据配置就绪。我在这个网站上看到的一个例子演示了一个submitTapped函数:
func submitTapped() {
guard username.text.characters.count > 0 else {
return
}
print("All good")
}
我想知道使用guard与使用if条件的老式方法是否有任何不同。它是否提供了简单支票无法获得的好处?
Swift 2引入了guard关键字,它可以用来确保各种数据配置就绪。我在这个网站上看到的一个例子演示了一个submitTapped函数:
func submitTapped() {
guard username.text.characters.count > 0 else {
return
}
print("All good")
}
我想知道使用guard与使用if条件的老式方法是否有任何不同。它是否提供了简单支票无法获得的好处?
当使用守护满足条件时,它将在守护块中声明的变量暴露给代码块的其余部分,将它们带入其作用域。如前所述,这将在嵌套的if let语句中派上用场。
注意,guard在其else语句中要求return或throw。
使用Guard解析JSON
下面是一个如何使用guard而不是if-let解析JSON对象的示例。这是一篇博客文章的节选,其中包括一个playground文件,你可以在这里找到:
如何使用守卫在Swift 2解析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)
}
下载操场:守卫操场
更多信息:
以下是Swift编程语言指南的节选:
If the guard statement’s condition is met, code execution continues after the guard statement’s closing brace. Any variables or constants that were assigned values using an optional binding as part of the condition are available for the rest of the code block that the guard statement appears in. If that condition is not met, the code inside the else branch is executed. That branch must transfer control to exit the code block that that guard statement appears in. It can do this with a control transfer statement such as return, break, or continue, or it can call a function or method that doesn’t return, such as fatalError().
这确实有两大好处需要保护。一个是避免厄运金字塔,就像其他人提到的那样——许多恼人的if语句嵌套在彼此之间,越来越向右移动。
另一个好处是你想要实现的逻辑通常是“if not let”而不是“if let {} else”。
这里有一个例子:假设你想要实现accumulate -一个map和reduce之间的交叉,它会返回一个运行的reduce数组。下面是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(+) // []
你怎么写它没有守卫,但仍然使用第一,返回一个可选?就像这样:
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 []
}
}
}
额外的嵌套是恼人的,而且,if和else相隔太远也不符合逻辑。让空case提前退出,然后继续函数的其余部分,就好像这是不可能的一样,可读性要高得多。
阅读这篇文章,我注意到使用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
}
在这里,您将所需的代码放在所有条件中 您可能不会立即发现这有什么问题,但是您可以想象,如果它嵌套了许多在运行语句之前都需要满足的条件,那么它会变得多么令人困惑
解决这个问题的方法是首先进行每一项检查,如果有任何不符合就退出。这样可以很容易地理解什么条件将使该函数退出。
但是现在我们可以使用guard,我们可以看到它可以解决一些问题:
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
}
检查你想要的条件,而不是你不想要的条件。这同样类似于断言。如果条件不满足, 运行Guard的else语句,跳出函数。 如果条件通过,这里的可选变量将自动在保护的范围内为您展开 语句被调用——在本例中是fooGuard(_:)函数。 您可以尽早检查坏的情况,使您的函数更易于阅读和维护
同样的模式也适用于非可选值:
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
}
如果你还有任何问题,你可以阅读整篇文章:Swift guard声明。
结束
最后,通过阅读和测试,我发现如果你使用guard来打开任何可选选项,
这些未包装的值将保留在您的其余部分中使用 代码块
.
guard let unwrappedName = userName else {
return
}
print("Your username is \(unwrappedName)")
在这里,打开的值只在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)")
用保护我们的意图是明确的。如果这个特定条件不满足,我们就不想执行其余的代码。 在这里我们也可以扩展链,请看看下面的代码:
guard let value1 = number1, let value2 = number2 else { return }
// do stuff here
它确实使具有多个查找和可选项的序列流更加简洁和清晰,并减少了大量if嵌套。参见Erica Sadun关于替换if的帖子. ....可能会忘乎所以,下面是一个例子:
let filteredLinks = locationsLinkedToList.filter({$0.actionVerb == movementCommand})
guard let foundLink = filteredLinks.first else {return ("<Person> cannot go in that direction.", nil, nil)}
guard filteredLinks.count == 1 else {return ("<Person> cannot decide which route to take.", nil, nil)}
guard let nextLocation = foundLink.toLocation else {return ("<Person> cannot go in that direction.", nil, nil)}
看看能不能坚持下去。
和if语句一样,guard也根据表达式的布尔值执行语句。与if语句不同,guard语句只在条件不满足时运行。您可以将守卫更像Assert,但是可以优雅地退出,而不是崩溃。
请参考: http://ericcerney.com/swift-guard-statement/
何时使用警卫
如果你有一个带有一些UITextField元素或一些其他类型的用户输入的视图控制器,你会立即注意到你必须展开textField。文本可选,以获得内部的文本(如果有!)。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) {
...
}
您会注意到,我们的服务器通信函数以非可选的String值作为参数,因此会提前展开保护。展开有点不直观,因为我们习惯使用if let来展开,它将值展开以在块中使用。这里guard语句有一个相关联的块,但它实际上是一个else块——也就是说,如果展开失败,你所做的事情——值被直接展开到与语句本身相同的上下文中。
//关注点分离
没有保护
如果不使用guard,我们最终会得到一大堆类似于末日金字塔的代码。这不能很好地扩展向表单添加新字段或使代码非常可读。缩进可能很难遵循,特别是在每个分支上有这么多其他语句。
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")
}
}
是的,我们甚至可以将所有这些语句合并为一个用逗号分隔的语句,但我们将失去找出哪条语句失败并向用户显示消息的能力。
https://thatthinginswift.com/guard-statement-swift/
简单地说,它提供了一种在执行之前验证字段的方法。这是一种很好的编程风格,因为它增强了可读性。在其他语言中,它可能是这样的:
func doSomething() {
if something == nil {
// return, break, throw error, etc.
}
...
}
但是因为Swift为你提供了可选项,我们不能检查它是否为nil并将其值赋值给一个变量。相反,if let检查它不是nil,并分配一个变量来保存实际值。这就是警卫发挥作用的地方。它为您提供了一种更简洁的方式,可以使用可选项提前退出。
警卫的陈述就要做了。这是两个不同的问题
1)它允许我减少嵌套的if语句 2)它是增加我的范围,我的变量可访问
if语句
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
}
保安声明
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
}
来自苹果文档:
保安声明
守卫语句用于在一个或多个条件不满足时将程序控制转移到作用域之外。
Synatx:
guard condition else {
statements
}
优势:
1. 通过使用guard语句,我们可以摆脱深度嵌套的条件,这些条件的唯一目的是验证一组需求。
2. 它是专门为提前退出方法或函数而设计的。
如果你使用If,下面是它看起来的代码。
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if error == nil {
if let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 {
if let data = data {
//Process Data Here.
print("Data: \(data)")
} else {
print("No data was returned by the request!")
}
} else {
print("Your request returned a status code other than 2XX!")
}
} else {
print("Error Info: \(error.debugDescription)")
}
}
task.resume()
如果一个或多个条件不满足,可以使用守卫将控制转移到作用域之外。
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
/* GUARD: was there an error? */
guard (error == nil) else {
print("There was an error with your request: \(error)")
return
}
/* GUARD: Did we get a successful 2XX response? */
guard let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 else {
print("Your request returned a status code other than 2XX!")
return
}
/* GUARD: was there any data returned? */
guard let data = data else {
print("No data was returned by the request!")
return
}
//Process Data Here.
print("Data: \(data)")
}
task.resume()
参考:
1. Swift 2:带着警卫早点离开 2. Udacity 3.保安声明
来源:《Swift》中的Guard
让我们看一个例子来清楚地理解它
示例1:
func validate() {
guard 3>2 else {
print ("False")
return
}
print ("True") //True
}
validate()
在上面的例子中,我们看到3大于2,守卫else子句中的语句被跳过并打印True。
示例2:
func validate() {
guard 1>2 else {
print ("False") //False
return
}
print ("True")
}
validate()
在上面的例子中,我们看到1小于2,守卫else子句内的语句被执行,False被打印出来,后面跟着return。
Example 3: gaurd let, unwrapping optionals through guard let
func getName(args myName: String?) {
guard let name = myName, !name.isEmpty else {
print ("Condition is false") // Condition is false return
}
print("Condition is met\(name)")
}
getName(args: "")
在上面的例子中,我们使用保护let来打开可选项。在函数getName中,我们定义了一个可选的字符串类型变量myName。然后使用守卫let检查变量myName是否为nil,如果没有赋值给name,再次检查,name不为空。如果两个条件都是合格的,即true, else块将被跳过并打印“条件符合名称”。
基本上,我们在这里检查两个用逗号分隔的东西,首先是展开和可选,然后检查它是否满足条件。
这里我们没有向函数传递任何东西,即空字符串,因此打印Condition为false。
func getName(args myName: String?) {
guard let name = myName, !name.isEmpty else {
print ("Condition is false")
return
}
print("Condition is met \(name)") // Condition is met Hello
} getName(args: "Hello")
在这里,我们将“Hello”传递给函数,您可以看到输出打印为“Condition is met Hello”。