Swift's guard关键字

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

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


print("All good")
}

我想知道使用guard是否与使用if条件的老式方法有任何不同。它是否提供了简单支票无法获得的好处?

121707 次浏览

一个好处是消除了大量嵌套的if let语句。请在15:30左右观看WWDC“Swift新功能”视频,其中一节名为“末日金字塔”。

if不同,guard创建的变量可以从其块外访问。打开大量__abc2是有用的。

当使用guard满足条件时,它将在guard块中声明的变量暴露给代码块的其余部分,将它们带入其作用域。如前所述,这将在嵌套if let语句中派上用场。

注意,guard在其else语句中需要返回

使用Guard解析JSON

下面是一个如何使用guard而不是if-let解析JSON对象的示例。这是一篇博客文章的节选,其中包括一个playground文件,你可以在这里找到:

如何在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)


}

下载操场:警卫队操场

更多信息:

下面是Swift编程语言指南:的摘录

如果guard语句的条件满足,代码继续执行 在guard语句的结束大括号之后。任何变量或常数 对象的可选绑定被赋值 条件下可用的其余代码块表示该保护 语句出现在。

如果该条件不满足,else分支中的代码为 执行。该分支必须转移控制以退出代码块 警卫的声明出现在。它可以通过控件来实现 转换语句,如return、break或continue,或者它可以调用 一个不返回的函数或方法,例如fatalError()

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(+)  // []

你怎么写它没有 guard,但仍然使用返回一个可选的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 []
}
}


}

额外的嵌套是恼人的,但同样,ifelse相隔如此之远也不符合逻辑。让空case提前退出,然后继续函数的其余部分,就好像这是不可能的一样,可读性要高得多。

在阅读这篇文章时,我注意到使用警卫有很大的好处

下面你可以用一个例子来比较guard的用法:

这是没有防备的部分:

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


// Value requirements not met, do something
}
  1. 在这里,您正在将所需的代码置于所有条件中

    您可能不会立即发现这有什么问题,但是您可以想象,如果它嵌套了许多在运行语句

    之前都需要满足的条件,那么它会变得多么令人困惑

解决这个问题的方法是首先进行每一项检查,如果有任何不符合就退出。这样可以很容易地理解什么条件将使该函数退出。

但是现在我们可以使用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
}
  1. 检查你想要的条件,而不是你不想要的。这同样类似于断言。如果条件不满足, 运行Guard的else语句,跳出函数
  2. 如果条件通过,这里的可选变量将自动在守卫的范围内为您展开 语句被调用——在本例中是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
}

如果你仍然有任何问题,你可以阅读整篇文章:迅速的警卫声明。

结束

最后,通过阅读和测试,我发现如果你使用guard来打开任何可选选项,

这些未包装的值将保留在您的其余部分中使用 代码块< / p >

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,但是可以优雅地退出,而不是崩溃。

< p >是指: http://ericcerney.com/swift-guard-statement/ < / p >

何时使用警卫

如果你有一个带有一些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检查它是否为不是零,并分配一个变量来保存实际值。这就是guard发挥作用的地方。它为您提供了一种更简洁的方式,可以使用可选项提前退出。

警卫的陈述就要做了。这是两个不同的问题

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


}

来自苹果文档:

保安声明

守卫语句用于在一个或多个条件不满足时将程序控制转移到作用域之外。

< em > Synatx: < / em >

guard condition else {
statements
}

< em >优势:< / em >

1. 通过使用guard语句,我们可以摆脱深层嵌套的条件,这些条件的唯一目的是验证一组需求。

2. 它是专门为提前退出方法或函数而设计的。

如果你使用if let下面是它看起来的代码

  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()

如果一个或多个条件不满足,使用guard可以将控制转移到作用域外

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()

参考:< em > < / em >

< em > 1。Swift 2: Exit Early With guard 2. Udacity 3.保安声明 < / em > < / p >

来源:斯威夫特的警卫

让我们看一个例子来清楚地理解它

示例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”。