我一直在阅读关于Swift中的Optionals,我已经看到了一些例子,其中if let
用于检查一个Optional是否包含一个值,如果有的话 - 用unwrapped值做一些事情。
但是,我已经看到在Swift 2.0中主要使用关键字guard
。我想知道if let
是否已从Swift 2.0中移除或是否仍然可以使用。
我应该将包含if let
的程序更改为guard
吗?
if let
和guard 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%的时间内工作......如果它失败了,我不知道为什么会这样;这是非常不可能......但是只需使用这个默认图像,或者只是用一个有意义的消息断言来描述出错的地方!
- 当它们产生副作用时避免使用
guard
s,将防护装置用作自然流动。当else
条款引入副作用时避免守卫。警卫为代码正确执行提供了必要的条件,提供early exit- 当您在正分支中执行重要计算时,从
if
重构为guard
语句并返回else
子句中的回退值
此外,由于上述建议和干净的代码,您更可能希望/需要将断言添加到失败的保护语句中,它只是提高了可读性,并使其他开发人员清楚地了解您的期望。
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
s使用断言/前置条件。它似乎不正确)
使用警卫还可以避免厄运的金字塔,从而帮助您提高清晰度。见Nitin's answer。
我认为没有人能够很好地解释一个重要的区别。
然而,guard
和if 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
(在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-let
s,因为失败路径已经全部返回。
我将尝试用一些(未经优化的)代码来解释保护语句的有用性。
您有一个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)
}
如果字段的顺序发生变化,只需向上或向下移动相应的代码行,就可以了。
这是一个非常简单的解释和用例。希望这可以帮助!
基本差异
注意:两者都用于解包Optional变量。
我看到的最清楚的解释是在Github Swift Style Guide:
if
增加了一定程度的深度:
if n.isNumber {
// Use n here
} else {
return
}
guard
没有:
guard n.isNumber else {
return
}
// Use n here
- 如果不满足一个或多个条件,则使用保护语句将程序控制转移到范围之外。
- guard语句中任何条件的值必须是Bool类型或桥接到Bool的类型。条件也可以是可选的绑定声明
保护声明具有以下形式:
guard condition else {
//Generally return
}
- 也像optional binding一样受欢迎
- 为了访问可选对象,我们使用if let
if let roomCount = optionalValue {
print("roomCount available")
} else {
print("roomCount is nil")
}
我从鲍勃那里迅速学到了这一点。
典型的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的问题
Guard语句只有在条件为false时才会运行一个保护块,它将通过return退出该函数。如果条件为真,则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
}
}
用Guard展开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展开多个选项 - 如果到目前为止,您已逐个展开选项。 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
}