斯威夫特:守卫vs如果让

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了斯威夫特:守卫vs如果让相关的知识,希望对你有一定的参考价值。

我一直在阅读关于Swift中的Optionals,我已经看到了一些例子,其中if let用于检查一个Optional是否包含一个值,如果有的话 - 用unwrapped值做一些事情。

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

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

答案

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

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

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

一般来说,如果if-let块将成为函数的其余部分,或者它的else子句中会有return或abort,那么你应该使用guard代替。这通常意味着(至少在我的经验中),如果有疑问,guard通常是更好的答案。但是有很多情况下if let仍然是合适的。

另一答案

警卫可以提高清晰度

当你使用后卫时,你对后卫的期望值要高得多,而且如果它没有成功,那么你只想提前退出范围,这一点有点重要。就像你守卫看文件/图像是否存在,如果数组是否为空。

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。但是,如果你使用防护,你会增加代码的清晰度,这意味着我希望它能在95%的时间内工作......如果它失败了,我不知道为什么会这样;这是非常不可能......但是只需使用这个默认图像,或者只是用一个有意义的消息断言来描述出错的地方!

  • 当它们产生副作用时避免使用guards,将防护装置用作自然流动。当else条款引入副作用时避免守卫。警卫为代码正确执行提供了必要的条件,提供early exit
  • 当您在正分支中执行重要计算时,从if重构为guard语句并返回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-lets使用断言/前置条件。它似乎不正确)

使用警卫还可以避免厄运的金字塔,从而帮助您提高清晰度。见Nitin's answer


Guard创建一个新变量

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

然而,guardif let都打开了变量

使用guard,您将创建一个将存在于else语句之外的新变量。

使用if let,您不会创建任何新变量 - 在else语句之后,如果可选项为非零,则只输入代码块。新创建的变量只存在于代码块内部而不是之后!

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的回答中也提到过):

你必须在func中定义guard。它的主要目的是在不满足条件时中止/返回/退出范围:

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!
}
另一答案

什么时候使用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-lets,因为失败路径已经全部返回。

另一答案

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

您有一个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.becomeFirstRes

以上是关于斯威夫特:守卫vs如果让的主要内容,如果未能解决你的问题,请参考以下文章

如何让 CATransaction 无限重复? - 斯威夫特

斯威夫特:AVAudioPCMBuffer vs AVAudioBuffer vs AVAudioCompressedBuffer

VS Code Intellisense 一开始不建议片段

vs 2010代码片段

vs 2010代码片段

在 VS2010 中使用 Nvidia NSight 进行 CUDA 性能分析 - 时间线上的片段