代码之家  ›  专栏  ›  技术社区  ›  lmiguelvargasf

斯威夫特:后卫让vs如果让

  •  192
  • lmiguelvargasf  · 技术社区  · 9 年前

    我一直在阅读Swift中的Optionals,我看到了一些例子 if let 用于检查Optional是否包含一个值,以防它对展开的值执行某些操作。

    然而,我在Swift 2.0中看到了关键字 guard let 主要使用。我想知道 如果让 已从Swift 2.0中删除,或者如果仍然可以使用。

    我应该更改包含 如果让 警卫室 ?

    10 回复  |  直到 4 年前
        1
  •  230
  •   Rob Napier    9 年前

    if let guard let 具有相似但不同的目的。

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

    如果让 嵌套其范围,不需要任何特殊的内容。它可以 回来 或者不。

    通常,如果 if-let 块将是函数的其余部分,或者 else 条款将有 回来 或者中止,那么你应该使用 警卫 相反这通常意味着(至少在我的经验中), 警卫 通常是更好的答案。但在很多情况下 如果让 仍然是合适的。

        2
  •  150
  •   mfaani    2 年前

    守卫可以提高 清晰

    当你使用守卫时,你有很多 较高的 对警卫的期望 达到目的 有一点很重要,如果它不成功,那么您只需要退出范围 早期的 。就像您要查看文件/图像是否存在,数组是否为空。

    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编写上述代码,它会向阅读开发人员传达一种五五开的感觉 清晰 这意味着我希望它能在95%的时间内工作……如果它失败了,我不知道为什么会失败;这是非常不可能的……但只需使用这个默认图像,或者只需使用一条有意义的消息来描述错误所在!

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

    • 当在正分支中执行重要计算时,从 if 警卫 语句,并返回回退值 在 其他的 条款

    发件人: Erica Sadun's Swift Style book

    由于以上建议和干净的代码 可能的 您需要将断言添加到 失败 guard语句,它只是提高了可读性,并向其他开发人员表明了您的期望。

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

    发件人: 艾丽卡·萨顿的《迅捷风格》一书 +一些修改

    (您不会使用断言/前提条件 if-let s、 只是看起来不太对)

    使用防护装置还可以通过以下方式提高清晰度 避开金字塔 厄运。看见 Nitin's answer .

    Guard将处理违规需求的代码保存在需求旁边

    为了清楚, 警卫 并不总是关于成功与失败。更通用的方法是处理违反的需求与未违反的流程代码。

    例子:

    func getImage(completion: (image: Image)? -> Void) {
       guard cache["image1"] == false else {
          completion(cache["image1"]!)
       }
       downloadAndStore("image1") { image in 
           completion(image)
       }
    }
    
    

    在上文中,要求图像不存在于缓存中。如果图像存在,则违反了我们的要求。我们很早就回来了。正如您所看到的,我们还处理了违反的代码路径,就在它的需求旁边,即结构不是:

    if requirement {
       . 
       . 
       ten lines of code
       . 
       . 
    } else {
       handle requirement
    }
    

    这个 Swift Docs on Control Flow 解释背后的想法:

    对需求使用保护语句可以提高 与使用if语句执行相同的检查相比。

    • 它允许您编写通常执行的代码,而无需将其包装在else块中
    • 它允许您将处理违规需求的代码放在需求旁边。

    Guard通过创建 当前范围中的变量

    有一个重要的区别,我相信没有人能很好地解释。

    二者都 guard let if let 打开…的包装 然而,变量

    具有 警卫室 正在创建 一个新变量 存在 在当前范围内。

    具有 如果让 你只是在创建一个新变量 在…内 代码块。

    guard let:

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

    有关更多信息 如果让 请参阅: Why redeclaration of optional binding doesn't create an error


    防护要求 作用域退出

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

    你必须有 警卫 定义 在…内 函数。其主要目的是中止/返回/退出范围, 如果 不满足条件:

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

    对于 如果让 你不需要把它放在任何函数中:

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

    警卫 如果

    值得注意的是,将此问题视为 警卫室 如果让 警卫 如果 .

    一个独立的 如果 不执行任何展开操作,独立的也不执行 警卫 。请参见下面的示例。如果值为 nil 。没有可选值。如果不满足条件,它就会提前退出。

    let array = ["a", "b", "c"]
    func subscript(at index: Int) -> String?{
       guard index > 0, index < array.count  else { return nil} // exit early with bad index
       return array[index]
    }
    
        3
  •  54
  •   Beltalowda    8 年前

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

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

    您可以使用 如果让 :

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

    或者像这样使用 警卫 :

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

    这些示例在功能上完全相同。

    哪里 警卫 真正的亮点是当您有一个像验证数据这样的任务时,如果出现任何错误,您希望函数尽早失败。

    而不是嵌套一堆 如果让 当您接近完成验证时,“成功路径”和现在成功绑定的选项都在方法的主范围内,因为失败路径都已经返回。

        4
  •  39
  •   Nitin Alabur    5 年前

    我将尝试用一些(未优化的)代码解释guard语句的有用性。

    您有一个UI,用于验证用户名、姓氏、电子邮件、电话和密码的用户注册文本字段。

    如果任何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语句的范围内访问。因此,它创建了这个“末日金字塔”,并存在许多问题,包括可读性和移动东西的方便性(如果字段的顺序被更改,则必须重写大部分代码)

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

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

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

        5
  •  21
  •   Manar Bushnaq Kiran K    4 年前

    基本区别

    警卫室

    1. 范围内的早期存在过程
    2. 需要现有范围,如 return , throw
    3. 创建一个可以在范围外访问的新变量。

    如果让

    1. 无法在作用域之外访问。
    2. 不需要 回来 陈述但我们可以写

    注意:这两个都用于展开可选变量。

        6
  •  7
  •   Anand Khanpara    4 年前

    后卫让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 ""
    }
    
        7
  •  6
  •   wildpeaks    7 年前

    我看到的最清楚的解释是 Github Swift Style Guide :

    if 添加深度级别:

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

    guard 不:

    guard n.isNumber else {
        return
    }
    // Use n here
    
        8
  •  6
  •   Abdurrahman Mubeen Ali    4 年前

    guard

    • A. guard 语句用于将程序控制转移到范围之外 如果一个或多个条件不满足。

    • 警卫 语句的类型必须为 Bool 或桥接到 布尔 。该条件也可以是可选的 具有约束力的声明。

    保护声明的格式如下:

    guard condition else {
        //Generally return
    }
    

    if let

    if let roomCount = optionalValue {
        print("roomCount available")
    } else {
        print("roomCount is nil")
    }
    
        9
  •  2
  •   OurangZeb Khan    6 年前

    我是从斯威夫特和鲍勃那里学到的。。

    典型的Else If

     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. 必须阅读每一行才能发现错误信息

    保护声明 只有当条件为false时,保护块才会运行,并且它将通过返回退出函数。如果条件为真,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展开选项

    guard语句不仅适用于用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
                               }
         }
    

    用防护装置展开 其他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展开多个选项 当然,我们应该在其他情况下保持警惕+

    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
    }
    
        10
  •  0
  •   Krishan Madushanka    2 年前

    swift中guard和if语句的主要区别是:

    • if语句用于在满足条件时运行代码。
    • guard语句用于在不满足条件时运行代码。