198

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

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

    print("All good")
}

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

4

13 回答 13

373

阅读这篇文章,我注意到使用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
}
  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
}

如果您还有任何疑问,您可以阅读整篇文章:Swift guard statement。

包起来

最后,阅读和测试我发现如果你使用 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)")
于 2015-06-16T12:09:24.600 回答
38

与 不同ifguard创建可以从其块外部访问的变量。Optional解开很多s很有用。

于 2015-06-11T21:37:46.477 回答
25

确实有两大好处guard。一个是避免厄运金字塔,正如其他人所提到的——许多if let嵌套在彼此内部的烦人的陈述越来越向右移动。

另一个好处通常是你想要实现的逻辑if not let比“ ”更“ if let { } else”。

这里有一个例子:假设你想实现accumulate——一个交叉map,它给你一个运行reducereduce的数组。这是与: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(+)  // []

你会如何在没有保护的情况下编写它,但仍然使用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 []
        }
    }

}

额外的嵌套很烦人,而且,将ifandelse分开这么远也不合逻辑。让空箱提前退出更具可读性,然后继续执行其余的功能,就好像这不可能一样。

于 2015-06-11T22:01:59.793 回答
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)

}

下载游乐场:守卫游乐场

更多信息:

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

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

如果不满足该条件,则执行 else 分支中的代码。该分支必须转移控制以退出出现该保护语句的代码块。它可以使用控制转移语句(例如 return、break 或 continue)来完成此操作,或者它可以调用不返回的函数或方法,例如作为致命错误()。

于 2015-06-11T21:55:48.960 回答
7

一个好处是消除了许多嵌套if let语句。请参阅 15:30 左右的 WWDC“Swift 中的新功能”视频,标题为“末日金字塔”的部分。

于 2015-06-11T21:12:06.687 回答
6

何时使用警卫

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

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

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) {
  ...
}

您会注意到我们的服务器通信函数将非可选的字符串值作为参数,因此预先展开了防护。展开有点不直观,因为我们习惯于用 if 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")
    }
}

是的,我们甚至可以将所有这些 if let 语句组合成一个用逗号分隔的单个语句,但是我们将失去找出哪个语句失败并向用户显示消息的能力。

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

于 2016-06-27T23:33:36.137 回答
5

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

guard let value1 = number1, let value2 = number2 else { return }
 // do stuff here
于 2015-09-24T10:54:22.850 回答
4

守卫声明要做。这是几个不同的

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

}
于 2016-09-18T16:34:25.723 回答
3

来自 Apple 文档:

守卫声明

如果不满足一个或多个条件,则使用保护语句将程序控制转移到范围之外。

语法:

guard condition else {
    statements
}

优势:

1. 通过 usingguard语句,我们可以摆脱深度嵌套的条件,其唯一目的是验证一组需求。

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

参考:

1. Swift 2: 提前退出 2. Udacity 3. Guard 声明

于 2016-11-17T07:14:24.183 回答
2

与 if 语句一样,guard 基于表达式的布尔值执行语句。与 if 语句不同,guard 语句仅在不满足条件时运行。你可以把守卫想象成一个 Assert,但与其崩溃,你可以优雅地退出。

参考: http ://ericcerney.com/swift-guard-statement/

于 2016-03-04T14:32:32.823 回答
1

它确实确实使具有多个查找和选项的序列流更加简洁明了,并减少了很多 if 嵌套。请参阅 Erica Sadun关于替换 Ifs的帖子。....可能会得意忘形,下面的例子:

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

看看能不能坚持。

于 2016-02-08T18:46:10.843 回答
1

简而言之,它提供了一种在执行之前验证字段的方法。这是一种很好的编程风格,因为它增强了可读性。在其他语言中,它可能如下所示:

func doSomething() {
    if something == nil {
        // return, break, throw error, etc.
    }
    ...
}

但是因为 Swift 为您提供了可选项,我们无法检查它是否为 nil并将其值分配给变量。相反,if let检查它不是 nil并分配一个变量来保存实际值。这就是guard发挥作用的地方。它为您提供了一种更简洁的方式来使用选项提前退出。

于 2016-07-17T14:43:47.043 回答
1

资料来源:Swift 中的 Guard

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

示例 1:

func validate() {         
    guard 3>2 else {             
    print ("False")             
    return         
    }         
    print ("True") //True     
} 
validate()

在上面的示例中,我们看到 3 大于 2,并且跳过了 guard else 子句中的语句并打印了 True。

示例 2:

func validate() {         
    guard 1>2 else {             
    print ("False")            //False 
    return         
    }         
    print ("True")      
} 
validate()

在上面的示例中,我们看到 1 小于 2,并且执行了 guard else 子句中的语句,并打印了 False,然后返回。

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: "")

在上面的例子中,我们使用了 guard let 来解开选项。在函数 getName 中,我们定义了一个字符串 myName 类型的变量,它是可选的。然后我们使用guard let来检查变量myName是否为nil,如果没有赋值给name,再次检查,name不为空。如果两个条件都符合条件,即为真,则将跳过 else 块并打印“条件符合名称”。

基本上我们在这里检查用逗号分隔的两件事,首先是展开和可选,然后检查它是否满足条件。

在这里,我们没有向函数传递任何内容,即空字符串,因此条件为假是打印。

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

于 2019-05-09T18:20:15.703 回答