Swift 斯威夫特:让人防守vs如果让人防守

Swift 斯威夫特:让人防守vs如果让人防守,swift,swift2,optional,conventions,control-flow,Swift,Swift2,Optional,Conventions,Control Flow,我一直在读Swift中的Optionals,我看到过一些例子,其中使用if let检查Optional是否有值,如果有,则使用未包装的值执行操作 然而,我已经看到,在Swift 2.0中,主要使用关键字guard let。我想知道let是否已从Swift 2.0中删除,或者是否仍然可以使用 我是否应该将包含if let的程序更改为guard let?if let和guard let具有类似但不同的用途 guard的“else”情况必须退出当前范围。通常这意味着它必须调用返回或中止程序guard用

我一直在读Swift中的Optionals,我看到过一些例子,其中使用
if let
检查Optional是否有值,如果有,则使用未包装的值执行操作

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


我是否应该将包含
if let
的程序更改为
guard let

if let
guard let
具有类似但不同的用途

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

if-let
嵌套了它的作用域,并且不需要任何特殊的内容。它可以
返回,也可以不返回


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

何时使用
如果让
以及何时使用
保护
通常是一个风格问题

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

如果让
,您可以使用
实现它:

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
    }
或者像这样使用
防护装置

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
    }
这些例子在功能上是相同的

guard
真正引人注目的地方是当您有一项任务,比如验证数据,并且如果出现任何错误,您希望函数尽早失败


当您接近完成验证时,不要嵌套一堆
if let
s,“成功路径”和现在成功绑定的选项都在该方法的主要范围内,因为失败路径已经全部返回。

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

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

如果任何文本字段不包含有效文本,则应使该字段成为第一响应者

以下是未优化的代码:

//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)
}
如果字段的顺序发生变化,只需向上或向下移动相应的代码行,就可以开始了

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

Guard可以提高清晰度 当您使用guard时,您对guard成功的期望值要高得多。如果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编写上面的代码,让它向阅读开发人员传达,它更像是一个50-50。但是如果你使用guard,你会增加代码的清晰度,这意味着我希望它能在95%的时间内工作……如果它失败了,我不知道为什么会失败;这是非常不可能的……但是,你可以使用这个默认图像来代替它,或者干脆使用它这是一条有意义的信息,描述了哪里出了问题

  • 避免
    guard
    s当它们产生副作用时,应将防护作为自然流使用。当
    else
    条款引入副作用时,应避免防护。 警卫为代码的正确执行建立必要的条件, 奉献

  • 在正分支中执行重要计算时,从
    if
    重构为
    guard
    语句并返回回退值 在
    else
    子句中

发件人:

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)
    }
}
同样由于上述建议和干净的代码,您更可能希望/需要将断言添加到失败的guard语句中,它只是提高了可读性,并向其他开发人员明确了您的期望

发件人:+一些修改

(如果让
s,则不会对
使用断言/前提条件。这似乎不正确)

使用守卫也可以通过避免金字塔毁灭来帮助你提高清晰度。请参阅


Guard创建一个新变量 有一个重要的区别,我相信没有人解释得很好

但是
guard let
if let
都可以打开变量

使用
guard let
可以创建一个新变量,该变量将存在于
else
语句之外

使用
if let
时,如果您没有在else语句之后创建任何新变量,则只有在可选项为非nil的情况下,您才可以输入代码块。新创建的变量只存在于代码块内部,而不在else语句之后

保护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)
}
如果让:
var str : String? if let blogName1 = str { print(blogName1) // You don't get any errors! }
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]
}
guard condition else {
    //Generally return
}
if let roomCount = optionalValue {
    print("roomCount available")
} else {
    print("roomCount is nil")
}
 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")
          }
     }
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.
       }
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")
                         }
                  }
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
                           }
     }
 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)
         }
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")
  }
}
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
}
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 ""
}