Swift的守卫关键词

时间:2015-06-11 21:09:02

标签: swift2 guard-statement

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

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

    print("All good")
}

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

13 个答案:

答案 0 :(得分:358)

阅读this article我注意到使用 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. 您可以在所有条件下使用所需的代码

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

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

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

    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.   
    3. 如果条件通过,则此处的可选变量将在防护范围内自动解包   语句被调用 - 在这种情况下,fooGuard(_ :)函数。
    4.   
    5. 您正在及早检查不良案例,使您的功能更易读,更易于维护
    6.   

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

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

答案 1 :(得分:35)

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

答案 2 :(得分:23)

<snippet> <content><![CDATA[ System.out.println(); ]]></content> <!-- Optional: Set a tabTrigger to define how to trigger the snippet --> <tabTrigger>pr</tabTrigger> <!-- Optional: Set a scope to limit where the snippet will trigger --> <scope>source.java</scope> </snippet> 确实有两大好处。一个是避免厄运的金字塔,正如其他人所提到的那样 - 许多恼人的guard陈述彼此嵌套,进一步向右移动。

另一个好处通常是您要实施的逻辑比“if let”更“if not let”。

以下是一个示例:假设您要实现if let { } else - accumulatemap之间的交叉,它会返回一个运行减少的数组。这是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(+) // [] ?像这样:

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 [] } } } if分开是不合逻辑的。对于空案例的早期退出更具可读性,然后继续使用函数的其余部分,就好像这不可能。

答案 3 :(得分:19)

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

请注意,警卫在其else语句中需要 return throw

使用Guard

解析JSON

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

<强> 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:

的摘录
  

如果满足guard语句的条件,则继续执行代码   在警卫声明的结束后。任何变量或常量   使用可选绑定作为一部分分配值   条件可用于守卫的其余代码块   声明出现在。

     

如果不满足该条件,则else分支内的代码为   执行。该分支必须转移控制以退出代码块   该守卫声明出现在。它可以通过控件执行此操作   转移声明,如返回,休息或继续,或者它可以呼叫   不返回的函数或方法,例如fatalError()。

答案 4 :(得分:7)

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

答案 5 :(得分:5)

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

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

答案 6 :(得分: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) {
  ...
}

您会注意到我们的服务器通信函数将非可选的String值作为参数,因此警卫会事先解开。展开是有点不直观的,因为我们习惯于解包如果let解包在块内使用的值。这里的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")
    }
}

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

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

答案 7 :(得分:5)

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

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

if Statement

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

}

答案 8 :(得分:3)

  

来自Apple文档:

警卫声明

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

<强> Synatx:

bDone

<强> 优势:

1。通过使用guard condition else { statements } 语句,我们可以摆脱深层嵌套的条件,其唯一目的是验证一组要求。

2。它专为早期退出方法或功能而设计。

如果您使用,请将以下代码视为代码。

guard

如果不满足一个或多个条件,使用防护可以将控制权移出范围。

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

参考:

1。 Swift 2: Exit Early With guard 2. Udacity 3. Guard Statement

答案 9 :(得分:2)

与if语句类似,guard基于表达式的布尔值执行语句。与if语句不同,只有在不满足条件时才会运行guard语句。你可以把警卫想象成一个Assert,但不是崩溃,你可以优雅地退出。

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

答案 10 :(得分:1)

它确实使具有多个查找和选项的序列流更加简洁明了,并且减少了很多嵌套。见Erica Sadun帖子on replacing 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)}

看看是否坚持。

答案 11 :(得分:1)

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

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

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

答案 12 :(得分:0)

来源:https://thenucleargeeks.com/2019/05/09/guard-in-swift/

让我们看一下示例以清楚地了解它

示例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 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不为空。如果两个条件都满足,即为true,则else块将被跳过,并显示“条件满足名称”。

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

在这里,我们没有向函数传递任何内容,即空字符串,因此print为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”传递给该函数,您可以看到输出已打印出“满足条件Hello”。