guard 的优势
Swift 2.0 带来了guard语句。但很多人还是不太理解guard的意义,特别是和 Swift 2.0 之前的简单if语句相比较。我们知道,所有的 guard 使用场景都可以用 if 来替代,那么为什么还要增加 guard 特性呢?其最最主要的原因还是 guard 语法的可读性更强。这里我们使用一个错误处理的例子,一个带有姓名和年龄的简单表格。
使用guard实现,代码为:
struct Person {
let name: String
var age: Int
}
struct PersonViewModel {
var name: String?
var age: String?
enum InputError: ErrorType {
case InputMissing
case AgeIncorrect
}
func createPerson() throws -> Person {
guard let age = age, let name = name
where name.characters.count > 0 && age.characters.count > 0 else {
throw InputError.InputMissing
}
guard let ageFormatted = Int(age) else {
throw InputError.AgeIncorrect
}
return Person(name: name, age: ageFormatted)
}
}
如果使用if实现createPerson()方法,代码为:
enum PersonResult {
case Success(Person)
case Failure(errorText: String)
}
func createPersonNoGuard() -> PersonResult {
if let age = age, let name = name
where name.characters.count > 0 && age.characters.count > 0
{
if let ageFormatted = Int(age) {
let person = Person(name: name, age: ageFormatted)
return PersonResult.Success(person)
} else {
return PersonResult.Failure(errorText: "The age is invalid!")
}
} else {
return PersonResult.Failure(errorText: "Information is Missing!")
}
}
和上面的 guard 实现相比,使用 guard 可以让我们的方法表意更加明确,更易于阅读,它能够表达“提前退出”的意图,提高了程序的健壮性。使用 guard 会强迫你编写 happy-path,如果出错会提前退出,从而必须处理可能发生的错误。换句话来理解这是 Swift 的设计者在暗示程序员在编写代码时时提早 return 的重要性。
这些情况下 guard 要慎用
但是,并不意味着要将所有的 if … else … 和 if let … 都替换成 guard 语法。guard 语法很容易被滥用和误用,并不是所有的代码层次结构中都适合 guard 的使用。
- 作为 if 的相反情况时慎用
作为 if 的相反情况可以理解为作用域内的代码只有在传递进来的条件被判断为 false 的时候才执行.
例如当我们需要判断一个闭包参数表列中的 error 参数是否有返回值的时候,当 error 为 nil 时我们才有必要执行作用域内的代码,反之 return。
这种情况下用 if 去实现就十分的清晰,可读性更高:
geoCoder.geocodeAddressString(textStr) { (placemarks : [CLPlacemark]?, error : NSError?) in
// 为了编码的安全性考虑,我们对于必要的参数都要进行判断
// 如果有 error 那么就退出
if error != nil {
print("error --- \(error)")
return
}
// 如果有结果,那么看一下结果是否为空,为空退出
guard let placemarks = placemarks else {
return
}
// 遍历所有的坐标(经纬度)
for place in placemarks {
print("wzywzywzy")
}
}
如果用 guard 去处理上面的 error,那么代码就变成了这样:
geoCoder.geocodeAddressString(textStr) { (placemarks : [CLPlacemark]?, error : NSError?) in
// 如果 error 存在,那么执行作用域内的代码
guard let error = error else {
// 如果有结果,那么看一下结果是否为空,为空退出
guard let placemarks = placemarks else {
return
}
// 遍历所有的坐标(经纬度)
for place in placemarks {
print("wzywzywzy")
}
}
// 作用域代码(存在 error,程序结束)
print("error --- \(error)")
return
}
显然,用 guard 去处理上述情况就非常的不妥,造成了 guard 的大括号内还嵌套了一个 for 循环,可读性大大降低。
换句话说,如果你想让这个参数有值并使用它,那么就用 guard,如果你不想让这个参数有值那么就用 if 去判断。这样就可以将错误以及之后的 return 集中在一个大括号内,而顺利执行的作用域代码就在大括号外了。增强了代码的可读性。
- 不要在 guard 的 else 语句中放入复杂代码
guard 的 else 语句中,不应该放大量的代码,除了简单的提前退出的语句外。如果你在 guard 的 else 代码块中用了其他的代码逻辑或是实现了任何实际功能,那么你就误用了 guard 了。
总之,除一些简单提前退出的语句和一些离开了当前函数的必要操作外,不应该有其他的代码。
最好在 guard 的 else 语句块内不要多过 2 ~ 3 行代码。
- 不要用 guard 去代替一些比较复杂的 if else 语句
下面一段代码采用 if else 实现:
var str : String? = "Hello"
if let helloStr = str where str!.hasPrefix("H") {
print(helloStr)
} else {
print("不符合H开头要求")
}
若采用 guard 去实现为:
var str : String? = "Hello"
guard let helloStr2 = str where str!.hasPrefix("H") else {
print("不符合H开头要求")
return
}
print(helloStr2)
对于这种简单的情况而言,使用 if else 语句比起没有分支的 guard 语法更加容易理解。
最后提醒一点:在使用 guard 的时候,请确保自己已经对可选链有一个正确的了解,guard 可以帮助我们避免使用显示的 if let 进行解包。
原文链接:
http://www.cocoachina.com/swift/20160908/17509.html
https://www.jianshu.com/p/9fff7621ed75