斯威夫特:守卫vs如果让

时间:2015-08-27 18:23:49

标签: swift swift2 control-flow convention

我一直在阅读关于Swift中Optionals的内容,我已经看到了if let用于检查Optional是否包含值的示例,以及它是否存在 - 使用未包装的值执行某些操作。

但是,我已经看到在Swift 2.0中主要使用关键字guard。我想知道是否已从Swift 2.0中移除了if let或者是否仍然可以使用它。

我应该将包含if let的程序更改为guard吗?

9 个答案:

答案 0 :(得分:126)

if letguard let提供相似但不同的目的。

guard的“else”情况必须退出当前范围。通常这意味着它必须调用return或中止程序。 guard用于提供早期返回,而不需要嵌套函数的其余部分。

if let占据其范围,并不需要任何特殊的东西。它可以return或不能。

通常,如果if-let块将成为函数的其余部分,或者其else子句中将包含return或中止,那么您应该使用而是guard。这通常意味着(至少根据我的经验),如果有疑问,guard通常是更好的答案。但是在很多情况下if let仍然合适。

答案 1 :(得分:81)

Guard可以提高清晰度

当你使用后卫时,你对成功的后卫有很多更高期望值,如果它没有成功,那么你只想退出范围早期。就像你守卫看文件/图像是否存在,如果数组是否为空。

func icon() -> UIImage {
    guard let image = UIImage(named: "Photo") else {
        return UIImage(named: "Default")! //This is your fallback
    }
    return image //-----------------you're always expecting/hoping this to happen
}

如果您使用if-let编写上述代码,它会向读取开发人员传达它更多的50-50。但是,如果你使用guard,你可以在你的代码中添加 clear ,这意味着我希望这可以在95%的时间内工作......如果它失败了,我不知道为什么会这样;这是非常不可能的...但是只需使用这个默认图像,或者只是用一个有意义的消息断言来描述出错的地方!

  
      
  • 当它们产生副作用时避免使用guard,将防护装置用作自然流程。当else条款引入副作用时避免使用警卫。   警卫为正确执行的代码建立必需的条件,   提供early exit

  •   
  • 在正分支中执行重要计算时,重构从ifguard语句并返回后备值   在else条款

  • 中   
     

发件人: Erica Sadun's Swift Style book

另外,由于上述建议和干净的代码,您可能希望/需要将断言添加到失败的保护语句中,这只是可能,它只是提高了可读性和向其他开发者明确表达了您的期望。

guard​ ​let​ image = ​UIImage​(named: selectedImageName) else { // YESSSSSS
     assertionFailure(​"Missing ​​\(​selectedImageName​)​​ asset"​) 
     return
} 

guard​ ​let​ image = ​UIImage​(named: selectedImageName) else { // NOOOOOOO
​     ​return 
}
     

来自: Erica Sadun's Swift Style book +一些修改

(你不会对if-let使用断言/先决条件。它似乎不正确)

使用警卫还可以帮助您通过避免厄运的金字塔来提高清晰度。请参阅Nitin's answer

Guard创建变量

我认为没有人能够很好地解释一个重要的区别。

然后guardif let 解开变量

使用guard正在创建一个存在else语句之外的新变量。

使用if let 未创建任何新变量 - 在else语句之后,只有输入代码块 if 可选的是非零。新创建的变量仅存在于内部代码块之后!

<强> guard:

func someFunc(blog: String?) {

    guard let blogName = blog else {
        print("some ErrorMessage")
        print(blogName) // will create an error Because blogName isn't defined yet
        return
    }
    print(blogName) // You can access it here ie AFTER the guard statement!!

    //And if I decided to do 'another' guard let with the same name ie 'blogName' then I would create an error!
    guard let blogName = blog else { // errorLine: Definition Conflicts with previous value.
        print(" Some errorMessage")
        return
    }
    print(blogName)
}

if-let:

func someFunc(blog: String?) {


    if let blogName1 = blog {
        print(blogName1) // You can only access it inside the code block. Outside code block it doesn't exist!
    }
    if let blogName1 = blog { // No Error at this line! Because blogName only exists inside the code block ie {}
        print(blogName1)
    }
}

有关if let的更多信息,请参阅:Why redeclaration of optional binding doesn't create an error

Guard要求范围退出

(在Rob Napier的回答中也提到过):

您必须 guard 在内定义一个func。它的主要目的是中止/返回/退出范围,如果不满足条件:

var str : String?

guard let blogName1 = str else {
    print("some error")
    return // Error: Return invalid outside of a func
}
print (blogName1)

对于 if let ,您无需将其置于任何func中:

var str : String?    
if let blogName1 = str {
   print(blogName1) // You don't get any errors!
}

答案 2 :(得分:44)

何时使用if-let以及何时使用guard通常是一种风格问题。

假设您有func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int和可选的项目数组(var optionalArray: [SomeType]?),如果数组为0(未设置),则需要返回nil如果数组有值(已设置),则为count

您可以使用 if-let

这样实现
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        if let array = optionalArray {
            return array.count
        }
        return 0
    }

或使用 guard

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        guard let array = optionalArray else {
            return 0
        }
        return array.count
    }

这些例子在功能上是相同的。

当你有一个像验证数据这样的任务时,guard真正闪耀的地方,如果出现任何问题,你希望函数提前失败。

当您接近完成验证时,不是嵌套一堆if-let,而是成功路径&#34;现在成功绑定的选项都在方法的主要范围内,因为失败路径已全部返回。

答案 3 :(得分:25)

我试着用一些(未优化的)代码来解释保护语句的用处。

您有一个用户界面,您可以使用名字,姓氏,电子邮件,电话和密码验证用户注册的文本字段。

如果任何textField不包含有效文本,则应将该字段设为firstResponder。

这是未经优化的代码:

//pyramid of doom

func validateFieldsAndContinueRegistration() {
    if let firstNameString = firstName.text where firstNameString.characters.count > 0{
        if let lastNameString = lastName.text where lastNameString.characters.count > 0{
            if let emailString = email.text where emailString.characters.count > 3 && emailString.containsString("@") && emailString.containsString(".") {
                if let passwordString = password.text where passwordString.characters.count > 7{
                    // all text fields have valid text
                    let accountModel = AccountModel()
                    accountModel.firstName = firstNameString
                    accountModel.lastName = lastNameString
                    accountModel.email = emailString
                    accountModel.password = passwordString
                    APIHandler.sharedInstance.registerUser(accountModel)
                } else {
                    password.becomeFirstResponder()
                }
            } else {
                email.becomeFirstResponder()
            }
        } else {
            lastName.becomeFirstResponder()
        }
    } else {
        firstName.becomeFirstResponder()
    }
}

您可以看到,所有字符串(firstNameString,lastNameString等)只能在if语句的范围内访问。所以它创造了这个厄运的金字塔&#34;并且它有许多问题,包括可读性和易于移动的东西(如果字段&#39;顺序被改变,你必须重写大部分代码)

使用guard语句(在下面的代码中),您可以看到这些字符串在{}之外可用,如果所有字段都有效,则使用这些字符串。

// guard let no pyramid of doom
func validateFieldsAndContinueRegistration() {

guard let firstNameString = firstName.text where firstNameString.characters.count > 0 else {
            firstName.becomeFirstResponder()
            return
        }
guard let lastNameString = lastName.text where lastNameString.characters.count > 0 else {
            lastName.becomeFirstResponder()
            return
        }
guard let emailString = email.text where emailString.characters.count > 3 && emailString.containsString("@") && emailString.containsString(".") else {
            email.becomeFirstResponder()
            return
        }
guard let passwordString = password.text where passwordString.characters.count > 7 else {
            password.becomeFirstResponder()
            return
        }

// all text fields have valid text
    let accountModel = AccountModel()
    accountModel.firstName = firstNameString
    accountModel.lastName = lastNameString
    accountModel.email = emailString
    accountModel.password = passwordString
    APIHandler.sharedInstance.registerUser(accountModel)
}

如果字段的顺序发生变化,只需向上或向下移动相应的代码行,就可以了。

这是一个非常简单的解释和用例。希望这有帮助!

答案 4 :(得分:7)

基本差异

警卫让

  1. 来自范围的早期存在过程
  2. 要求得分如返回,投掷等。
  3. 创建一个可以访问范围的新变量。
  4. 如果让

    1. 无法访问范围。
    2. 无需退回声明。但我们可以写
    3. 注意:两者都用于解包可选变量。

答案 5 :(得分:2)

Guard

  
      
  • 使用guard语句将程序控制转移到范围之外   如果不符合一个或多个条件。

  •   
  • 保护声明中任何条件的值必须是Bool类型   或者是Bool的桥梁。条件也可以是可选的   约束声明

  •   

警卫声明的格式如下:

guard condition else {
    //Generally return
}

if let

  
      
  • 也很受欢迎optional binding
  •   
  • 要访问可选对象,我们使用 if
  •   
if let roomCount = optionalValue {
       print("roomCount available")
    } else {
        print("roomCount is nil")
 }

答案 6 :(得分:1)

我看到的最明确的解释是Github Swift Style Guide

if增加了深度:

if n.isNumber {
    // Use n here
} else {
    return
}

guard没有:

guard n.isNumber else {
    return
}
// Use n here

答案 7 :(得分:0)

我从鲍勃那里迅速学到了这一点。

典型的其他 - 如果

 func checkDrinkingAge() {
      let canDrink = true

     if canDrink {
        print("You may enter")
       // More Code
        // More Code
      // More Code

         } else {
         // More Code
    // More Code
    // More Code
    print("Let me take you to the jail")
          }
     }

Else-If

的问题
  1. 嵌套括号
  2. 必须阅读每一行以发现错误消息
  3. 警卫声明 保护块仅在条件为假时运行,并且将通过返回退出函数。如果条件为真,则Swift忽略保护块。它提供了早期退出和更少的括号。+

    func checkDrinkProgram() {
           let iCanDrink = true
    
               guard iCanDrink else {
            // if iCanDrink == false, run this block
             print("Let's me take you to the jail")
              return
            }
    
             print("You may drink")
               // You may move on
                      // Come on.
                     // You may leave
                    // You don't need to read this.
                     // Only one bracket on the bottom: feeling zen.
           }
    

    使用Else-If

    打开选项

    保护语句不仅可以用else-if语句替换典型的条件块,而且还可以通过最小化括号数来解包选项。为了进行比较,让我们首先开始如何使用else-if解包多个选项。 首先,让我们创建三个将被解开的选项。

    var publicName: String? = "Bob Lee"
    var publicPhoto: String? = "Bob's Face"
    var publicAge: Int? = nil
    

    最糟糕的噩梦

    func unwrapOneByOne() {
             if let name = publicName {
                  if let photo = publicPhoto {
                         if let age = publicAge {
                            print("Bob: \(name), \(photo), \(age)")
                                      } else {
                              print("age is mising")
                               }
                      } else {
                          print("photo is missing")
                             }
                      } else {
                            print("name is missing")
                             }
                      }
    

    上面的代码肯定有效,但违反了DRY原则。这太残暴了。让我们分解吧。+

    略好 下面的代码比上面的代码更具可读性。+

    func unwrapBetter() {
             if let name = publicName {
           print("Yes name")
                       } else {
                   print("No name")
            return
          }
    
             if let photo = publicPhoto {
                 print("Yes photo")
                } else {
               print("No photo")
           return
                 }
    
            if let age = publicAge {
                print("Yes age")
                          } else {
                    print("No age")
                return
                               }
         }
    

    用卫兵打开 else-if语句可以用guard替换。+

     func unwrapOneByOneWithGuard() {
                 guard let name = publicName else {
                      print("Name missing")
                  return
                                            }
    
                  guard let photo = publicPhoto else {
                  print("Photo missing")
                    return
                                                }
    
                      guard let age = publicAge else {
                       print("Age missing")
                                         return
                                                     }
                     print(name)
                     print(photo)
                     print(age)
             }
    

    使用Else-If 打开多个选项 到目前为止,您已经逐一展开选项。 Swift允许我们一次打开多个选项。如果其中一个包含nil,它将执行else块。

    func unwrap() {
      if let name = publicName, let photo = publicPhoto, let age = publicAge {
        print("Your name is \(name). I see your face right here, \(photo), you are \(age)")
      } else {
        // if any one of those is missing
        print("Something is missing")
      }
    }
    
      

    请注意,当您一次打开多个选项时,您无法识别哪个包含nil

    用Guard打开多个Optionals 当然,我们应该使用guard over else-if。+

    func unwrapWithGuard() {
      guard let name = publicName, let photo = publicPhoto, let age = publicAge else {
        // if one or two of the variables contain "nil"
        print("Something is missing")
        return
      }
    
      print("Your name is \(name). I see your, \(photo). You are \(age).")
      // Animation Logic
      // Networking
      // More Code, but still zen
    }
    

答案 8 :(得分:0)

警卫让vs如果让

func anyValue(_ value:String?) -> String {
    
    guard let string = value else {
        return ""
    }
    
    return string
}

func anyValue(_ value:String?) -> String {
    
    if let string = value {
        return string
    }
    
    return ""
}