代碼寫(xiě)多了就想優(yōu)化,這是一個(gè)天然的過(guò)程。近期在代碼優(yōu)化方面積累了一些心得,會(huì)慢慢整理出來(lái)。
本文主要適用于想要縮減代碼行數(shù)及規(guī)范化邏輯和錯(cuò)誤的場(chǎng)景。
首先回憶下在OC中是如何處理錯(cuò)誤和邏輯的,下面羅列兩種常見(jiàn)的處理方式。
1、方法定義返回值,根據(jù)返回值判斷成功還是失敗。復(fù)雜情況下定義枚舉可以覆蓋更多業(yè)務(wù)場(chǎng)景。
+ (BOOL) isFileExist:(NSString *)filePath
{
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL result = [fileManager fileExistsAtPath:filePath];
return result;
}
2、通過(guò)NSError的指針寫(xiě)入,判斷NSError不為空獲取錯(cuò)誤信息。
NSError *error;
BOOL success = [data writeToFile: path options: options error: &error];
if(error) {
// 發(fā)生了錯(cuò)誤
}
但實(shí)際情況是,很多時(shí)候不會(huì)出什么問(wèn)題,所以不少開(kāi)發(fā)會(huì)圖省事直接給error賦值nil。
下面介紹Swift中高逼格的用法。也就是關(guān)鍵字 throws和guard的應(yīng)用。
簡(jiǎn)單描述下例子場(chǎng)景:編寫(xiě)一個(gè)方法,通過(guò)Index獲取數(shù)組中的對(duì)象。
let item = array[index]
在一般情況下為了代碼可靠性和健壯性,會(huì)做一些非空判斷和邏輯判斷。根據(jù)返回值來(lái)絕對(duì)本次操作是否成功。
func getObjectByIndex(index:Int) -> Int {
let array = [1,2,3,4,5]
if index > 0{
if array.count > 0{
if index < array.count {
return array[index]
}
}
}
return -1
}
這里有三層判斷,但在實(shí)際項(xiàng)目開(kāi)發(fā)中我見(jiàn)過(guò)10層以上的嵌套,以至于后面版本迭代邏輯時(shí)很容易挑錯(cuò)在哪個(gè)代碼塊里。
而且即使最后返回-1,誰(shuí)也不敢保證數(shù)據(jù)源里真的有一個(gè)合法的-1被正確的返回出來(lái)了。也許有人會(huì)想到定義枚舉來(lái)更細(xì)致的區(qū)分錯(cuò)誤,但這個(gè)例子中又和返回?cái)?shù)據(jù)沖突了。。。。
Swift中使用guard配合throws可以很便捷的解決這個(gè)問(wèn)題
guard的知識(shí)比較基礎(chǔ),已經(jīng)了解的同學(xué)可以跳過(guò)直接往下看。
先說(shuō)guard的用法,字面意思是守護(hù) 警衛(wèi)。
很形象的比喻:當(dāng)你走進(jìn)一個(gè)大門,門口一個(gè)警衛(wèi)站著,看你有問(wèn)題就攔下你,沒(méi)問(wèn)題就放你過(guò)去。
當(dāng)guard關(guān)鍵字后的表達(dá)式為false時(shí),就會(huì)執(zhí)行else后的代碼塊,否則就繼續(xù)往下執(zhí)行
//條件為true,else后的代碼塊不會(huì)執(zhí)行
guard 1 == 1 else { return }
//條件為false,else后的代碼塊會(huì)執(zhí)行
guard 1 < 1 else { return }
很好理解不是么,熟練應(yīng)用后可以有效減少代碼的嵌套。
繼續(xù)改造上面的例子,首先定義一些錯(cuò)誤類型的枚舉。
enum arrayError: Error {
case indexCrossBoard, indexLessZero, arrayIsEmpty
}
注:在Swift4.0中已經(jīng)取消了ErrorType關(guān)鍵字。目前統(tǒng)一繼承Error。
接下來(lái)在入?yún)⒑竺嬷屑尤?code>throws關(guān)鍵字標(biāo)記該方法,在方法體內(nèi)部使用throw拋出具體的錯(cuò)誤類型。
func getObjByIndex(index:Int) throws -> Int {
let array = [1,2,3,4,5]
//when false,execute code in black after else
guard index < array.count else { throw arrayError.indexCrossBoard }
guard index > 0 else { throw arrayError.indexLessZero }
guard array.count > 0 else { throw arrayError.arrayIsEmpty }
return array[index]
}
可以看到值返回和錯(cuò)誤返回已經(jīng)被區(qū)分開(kāi)了,當(dāng)一個(gè)方法體被throws關(guān)鍵字標(biāo)記后的方法代表它可能會(huì)向外拋出錯(cuò)誤,這個(gè)錯(cuò)誤可以是自定義的,可以取自上面自定義的枚舉。
有拋出就一定有接收,所以方法在被調(diào)用時(shí)會(huì)被強(qiáng)制加上do catch try關(guān)鍵字,不然編譯器會(huì)報(bào)錯(cuò)。
do {
let item:Int = try getObjByIndex(index: 20)
} catch arrayError.indexCrossBoard {
print("Error of Corss board")
} catch arrayError.indexLessZero {
print("Error of Index less Zero")
} catch arrayError.arrayIsEmpty {
print("Array is Empty")
} catch {
}
相比較傳統(tǒng)的NSError處理錯(cuò)誤,這樣的規(guī)范使得開(kāi)發(fā)無(wú)法漠視操作中可能會(huì)帶來(lái)的錯(cuò)誤。比如磁盤滿了,但任然嘗試寫(xiě)入文件,排查了半天又不知道哪里出問(wèn)題了,身邊又圍了很多QA和產(chǎn)品,你懂的。
而這種做法表面上看try catch一定程度上冗長(zhǎng)了代碼,但回想下我們之前處理不同的錯(cuò)誤類型不也是要嵌套很多if判斷來(lái)執(zhí)行不同操作么。所以代碼優(yōu)化只是一定程度上的規(guī)整和增加可讀性,該做的事還是少不了的。
以上是入門級(jí)用法,我們還可以嘗試進(jìn)行閉包拋出錯(cuò)誤。
//Use throws mark Closure
typealias ArrayErrorCallback = () throws -> Bool
func checkObjectById(index:Int, errorBlock:@escaping (_ inner:ArrayErrorCallback) -> Void) {
let array = [1,2,3,4,5]
if index < array.count {
// throw error
errorBlock({ throw arrayError.indexCrossBoard })
}
// return value
errorBlock({return true})
}
override func viewDidLoad() {
super.viewDidLoad()
checkObjectById(index:20) { (inner: ArrayErrorCallback) -> Void in
do {
let success = try inner()
print(success)
} catch {
print(error)
}
}
}
用法差不多,只是把throws標(biāo)記在閉包上。更適合異步操作的場(chǎng)景。
基本就是這么多,大家可以回去翻閱下自己項(xiàng)目中的業(yè)務(wù)常見(jiàn),看哪些地方適合這樣的改動(dòng),本質(zhì)上還是以適合為主,不建議強(qiáng)行裝X。