Swift的守卫关键字

Swift's guard keyword

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

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

    print("All good")
}

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

一个好处是消除了很多嵌套的 if let 语句。请参阅有关 15:30 的 WWDC "What's New in Swift" 视频,标题为 "Pyramid of Doom" 的部分。

if 不同,guard 创建的变量可以从其块外部访问。解开很多 Optional 是很有用的。

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

注意 guard 在它的 else 语句中需要 returnthrow

使用 Guard

解析 JSON

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

How to use Guard in Swift 2 to parse 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)

}

下载游乐场:guard playground

更多信息:

以下是 The Swift Programming Language Guide:

的摘录

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

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

另一个好处是您要实现的逻辑通常是“if not let”而不是“if let { } else”。

这是一个例子:假设你想实现 accumulatemapreduce 之间的交叉,它返回一个 [=38 的数组=]减少。这是 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,returns 一个可选的?像这样:

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 提早退出,然后继续执行函数的其余部分,就好像那是不可能的那样。

阅读 this article 我发现使用 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. Checking for the condition you do want, not the one you don’t. This again is similar to an assert. If the condition is not met, guard‘s else statement is run, which breaks out of the function.
  2. If the condition passes, the optional variable here is automatically unwrapped for you within the scope that the guard statement was called – in this case, the fooGuard(_:) function.
  3. You are checking for bad cases early, making your function more readable and easier to maintain

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

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 来解包任何可选值,

those unwrapped values stay around for you to use in the rest of your code block

.

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 我们的意图很明确。如果不满足特定条件,我们不想执行其余代码。 这里我们也可以扩展链,请看下面的代码:

guard let value1 = number1, let value2 = number2 else { return }
 // do stuff here

它确实确实使具有多个查找和可选值的序列流更加简洁明了,并减少了很多 if 嵌套。参见 Erica Sadun post on replacing Ifs。 ....可能会被带走,下面是一个例子:

    let filteredLinks = locationsLinkedToList.filter({[=10=].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 语句仅 运行。你可以把 guard 想成更像一个 Assert,而不是崩溃,你可以优雅地退出。

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

何时使用守卫

如果你有一个带有一些 UITextField 元素或一些其他类型的用户输入的视图控制器,你会立即注意到你必须打开 textField.text 可选项才能获得里面的文本(如果任何!)。 isEmpty 在这里对你没有任何好处,没有任何输入,文本字段将简单地 return 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) {
  ...
}

您会注意到我们的服务器通信函数将非可选字符串值作为参数,因此守卫会提前解包。解包有点不直观,因为我们习惯于使用 if let 解包,它解包值以供在块内使用。这里的 guard 语句有一个关联的块,但它实际上是一个 else 块——即如果解包失败你要做的事情——值被直接解包到与语句本身相同的上下文中。

// 关注点分离

没有守卫

如果不使用 guard,我们最终会得到一大堆类似于 厄运金字塔 的代码。这对于向我们的表单添加新字段或生成非常易读的代码来说效果不佳。缩进可能很难理解,尤其是每个分叉处都有这么多 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/

简单地说,它提供了一种在执行前验证字段的方法。这是一种很好的编程风格,因为它增强了可读性。在其他语言中,它可能看起来像这样:

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

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

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

}

From Apple documentation:

保护声明

如果一个或多个条件不满足,保护语句用于将程序控制转移出范围。

语法:

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

如果一个或多个条件不满足,您可以使用 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: Exit Early With guard 2. Udacity 3. Guard Statement

来源:Guard in Swift

看例子就明白了

示例 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 后跟 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: "")

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

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

这里我们没有向函数传递任何内容,即空字符串,因此 Condition is false is print.

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