本頁包含內(nèi)容:
可選類型是Swift中新引入的,功能很強(qiáng)大。在這篇博文里討論的,是在Swift里,如何通過可選類型來保證強(qiáng)類型的安全性。作為例子,我們來創(chuàng)建一個Objective-C API的Swift版本,但實(shí)際上Swift本身并不需要這樣的API。
在Objective-C中,NSDictionary
有一個方法-objectsForKeys:NoFoundMarker:
, 這個方法需要一個NSArray
數(shù)組作為鍵值參數(shù),然后返回一個包含相關(guān)值的數(shù)組。文檔里寫到:"返回?cái)?shù)組中的第N個值,和輸入數(shù)組中的第N個值相對應(yīng)",那如果有某個鍵值在字典里不存在呢?于是就有了notFoundMarker
作為返回提示。比如第三個鍵值沒有找到,那么在返回?cái)?shù)組中第三個值就是這個notFoundMarker
,而不是字典中的第三個值,但是這個值只是用來提醒原字典中沒有找到對應(yīng)值,但在返回?cái)?shù)組中該元素存在,且用notFoundMarker
作為占位符,因?yàn)檫@個對象不能直接使用,所以在Foundation框架中有個專門的類處理這個情況:NSNull
。
在Swift中,Dictionary
類沒有類似objectsForKeys
的函數(shù),為了說明問題,我們動手加一個,并且使其成為操作字典值的通用方法。我們可以用extension
來實(shí)現(xiàn):
extension Dictionary{
func valuesForKeys(keys:[K], notFoundMarker: V )->[V]{
//具體實(shí)現(xiàn)代碼后面會寫到
}
}
以上就是我們實(shí)現(xiàn)的Swift版本,這個和Objective-C版本有很大區(qū)別。在Swift中,因?yàn)槠鋸?qiáng)類型的原因限制了返回的結(jié)果數(shù)組只能包含單一類型的元素,所以我們不能放NSNull
在字符串?dāng)?shù)組中,但是,Swift有更好的選擇,我們可以返回一個可選類型數(shù)據(jù)。我們所有的值都封包在可選類型中,而不是NSNull
, 我們只用nil
就可以了。
extension Dictionary{
func valuesForKeys(keys: [Key]) -> [Value?] {
var result = [Value?]()
result.reserveCapacity(keys.count)
for key in keys{
result.append(self[key])
}
return result
}
}
小伙伴們可能會問,為什么Swift中不需要實(shí)現(xiàn)這么一個API呢?其實(shí)其有更簡單的實(shí)現(xiàn),如下面代碼所示:
extension Dictionary {
func valuesForKeys(keys: [Key]) -> [Value?] {
return keys.map { self[$0] }
}
}
上述方式實(shí)現(xiàn)的功能和最開始的方法實(shí)現(xiàn)的功能相同,雖然核心的功能是封裝了map
的調(diào)用,這個例子也說明了為什么Swift沒有提供輕量級的API接口,因?yàn)樾』锇閭兒唵蔚恼{(diào)用map
就可以實(shí)現(xiàn)。
接下來,我們實(shí)驗(yàn)幾個例子:
var dic: Dictionary = [ "1": 2, "3":3, "4":5 ]
var t = dic.valuesForKeys(["1", "4"])
//結(jié)果為:[Optional(2), Optional(5)]
var t = dict.valuesForKeys(["3", "9"])
// 結(jié)果為:[Optional(3), nil]
t = dic.valuesForKeys([])
//結(jié)果為:[]
現(xiàn)在,如果我們?yōu)槊恳粋€結(jié)果調(diào)用last
方法,看下結(jié)果如何?
var dic: Dictionary = [ "1": 2, "3":3, "4":5 ]
var t = dic.valuesForKeys(["1", "4"]).last //結(jié)果為:Optional(Optional(5))
// Optional(Optional("Ching"))
var t = dict.valuesForKeys(["3", "9"]).last
// 結(jié)果為:Optional(nil)
var t = dict.valuesForKeys([]).last
// 結(jié)果為:nil
小伙伴們立馬迷糊了,為什么會出現(xiàn)兩層包含的可選類型呢?,特別對第二種情況的Optional(nil)
,這是什么節(jié)奏?
我們回過頭看看last
屬性的定義:
var last:T? { get }
很明顯last
屬性的類型是數(shù)組元素類型的可選類型,這種情況下,因?yàn)樵仡愋褪?code>(String?),那么再結(jié)合返回的類型,于是其結(jié)果就是String??
了,這就是所謂的嵌套可選類型。但嵌套可選類型本質(zhì)是什么意思呢?
如果在Objective-C中重新調(diào)用上述方法,我們將使用NSNull
作為占位符,Objective-C的調(diào)用語法如下所示:
[dict valuesForKeys:@[@"1", @"4"] notFoundMarker:[NSNull null]].lastObject
// 5
[dict valuesForKeys:@[@"1", @"3"] notFoundMarker:[NSNull null]].lastObject
// NSNull
[dict valuesForKeys:@[] notFoundMarker:[NSNull null]].lastObject
// nil
不管是Swift版本還是Objective-C版本,返回值為nil
都意味數(shù)組是空的,所以它就沒有最后一個元素。 但是如果返回是Optional(nil)
或者Objective-C中的NSNull
都表示數(shù)組中的最后一個元素存在,但是元素的內(nèi)容是空的。在Objective-C中只能借助NSNull
作為占位符來達(dá)到這個目的,但是Swift卻可以語言系統(tǒng)類型的角度的實(shí)現(xiàn)。
進(jìn)一步封裝,如果我字典中的某個或某些元素不存在,我們想提供一個默認(rèn)值怎么辦呢?實(shí)現(xiàn)方法很簡單:
extension Dictionary {
func valuesForKeys( keys:[Key], notFoundMarker: Value)->[Value]{
return self.valueForKeys(kes).map{ $0 ?? notFoundMarker }
}
}
dict.valuesForKeys(["1", "5"], notFoundMarker: "Anonymous")
和Objective-C相比,其需要占位符來達(dá)到占位的目的,但是Swift卻已經(jīng)從語言類型系統(tǒng)的層面原生的支持了這種用法,同時提供了豐富的語法功能。這就是Swift可選類型的強(qiáng)大之處。同時注意上述例子中用到了空合運(yùn)算符??
。
本章節(jié)不是老碼的原創(chuàng),是老碼認(rèn)真的閱讀了蘋果的官方博客,自己的練習(xí)總結(jié),如果小伙伴們費(fèi)了吃奶的勁還是看不懂,請找度娘谷歌。還是看不懂?請到老碼官方微博咆哮。