這篇文章將帶我們探索在Swift中如何使用Optional類型保證強類型的安全性。我們將創(chuàng)建一個Swift版本的Objective-C的API。雖然在Swift中這個API存在的意義不是很大,但是這將會是一個很有趣的例子。
在Objective-C中,NSDictionary類有一個方法,名為-objectsForKeys:notFoundMarker:,它的作用是根據(jù)第一NSArray類型參數(shù)中的值作為該字典的key,查找這些key對應的字典中的值并放到一個新的NSArray中返回,如果找不到對應的值,那么就返回第二個參數(shù)指定的對象。在官方文檔中對該方法有這么一句描述“返回的數(shù)組中的第N個對象,對應著第一個數(shù)組參數(shù)中的第N個值”。假如說以第一個數(shù)組參數(shù)中的第三個值作為key在字典中查不到值怎么辦呢?這時候就需要notFoundMarker參數(shù)登場了。這種情況下就會返回notFoundMarker參數(shù)指定的對象了。在Foundation框架中還有專門針對該情況適用的一個類,那就是NSNull,就是當你也沒有備選返回對象的時候,就可以返回NSNull對象。
在Swift中,Dictionary類型并沒有類似objectsForKeys的方法。所以接下來的練習中,我們將使用類型的擴展機制為Dictionary類型添加一個類似objectsForKeys的方法,為保持Swift的風格我們起名為valuesForKeys:notFoundMarker:。
extension Dictionary {
func valuesForKeys(keys: [K], notFoundMarker: V) -> [V] {
// To be implemented
}
}
在Swift中實現(xiàn)該方法與Objective-C有點不同,因為Swift中強類型的特性使返回的數(shù)組中只能包含某一種類型的元素,也就是說我們不能在一個字符串數(shù)組中添加一個NSNull類型的元素,這就使notFoundMarker的參數(shù)類型顯得非常尷尬。這怎么解決呢?別著急,在Swift中我們有更好的選擇:我們可以返回一個Optional類型的數(shù)組。從Dictionary中查出的值全部被包在Optional類型中,這樣當使用的key沒有對應值的時候,我們就可以使用nil來替代NSNull類型了。
extension Dictionary {
func valuesForKeys(keys: [Key]) -> [Value?] {
var result = [Value?]()
result.reserveCapacity(keys.count)
for key in keys {
result.append(self[key])
}
return result
}
}
注意:此時可能已經(jīng)有人認為Dictionary類型中的這個方法可能沒必要寫的這么繁瑣,你們可能已經(jīng)想到了這種情形:
extension Dictionary {
func valuesForKeys(keys: [Key]) -> [Value?] {
return keys.map { self[$0] }
}
}
這段代碼和上面那段代碼的作用和結(jié)果是完全一樣的,當keys調(diào)用map方法時,其實已經(jīng)將查出的所有值都包在了Optional類型中了。這就足以說明了為什么Swift中類型的API都那么短小精干,因為實現(xiàn)復雜邏輯就像上述代碼中直接調(diào)用map方法一樣簡單。
現(xiàn)在我們可以試著用我們擴展的方法做一些例子:
let dict = ["A": "Amir", "B": "Bertha", "C": "Ching"]
dict.valuesForKeys(["A", "C"])
// [Optional("Amir"), Optional("Ching")]
dict.valuesForKeys(["B", "D"])
// [Optional("Bertha"), nil]
dict.valuesForKeys([])
// []
內(nèi)嵌Optional類型
現(xiàn)在我們來看看使用last屬性返回數(shù)組的最后一個元素會發(fā)生什么?
dict.valuesForKeys(["A", "C"]).last
// Optional(Optional("Ching"))
dict.valuesForKeys(["B", "D"]).last
// Optional(nil)
dict.valuesForKeys([]).last
// nil
看著結(jié)果我們是不是覺得很奇怪呢?我們在上述代碼的第一種情況下得到了嵌套的Optional類型,而在第二種情況下缺得到了包含nil
的Optional類型,為什么得到的不是Optional("Ching")和nil呢?
冷靜下來,我們回憶一下last屬性的是如何申明的:
var last: T? { get }
恍然大悟,原來last屬性的類型是Optional類型,這也就是說如果T是Optional類型的話,那么T?自然就是Optional(Optional)了,也就是T??。所以上面的情況就很容易解釋了,因為T的類型Optional(String),所以我們得到的結(jié)果就Optional(Optional(String))。
那么Optional(nil)這種情況如何解釋呢?為什么不是Optional(Optional(nil))呢?
我們現(xiàn)在Objective-C中執(zhí)行一下上面那三種情況看一看:
[dict valuesForKeys:@[@"A", @"C"] notFoundMarker:[NSNull null]].lastObject
// @"Ching"
[dict valuesForKeys:@[@"B", @"D"] notFoundMarker:[NSNull null]].lastObject
// NSNull
[dict valuesForKeys:@[] notFoundMarker:[NSNull null]].lastObject
// nil
我們看到,不論在Swift中還是在Objective-C中,當?shù)谝粋€參數(shù)的數(shù)組是空數(shù)組的時候,取最后一個元素的返回結(jié)果都是nil,意思就是“數(shù)組是空數(shù)組,那么最后一個元素肯定不存在啦”。那么在Swift中返回Optional(nil)和在Objective-C中返回NSNull的情況表明這個所謂的最后一個元素在數(shù)組中其實是存在的,只不過它就代表沒有。當這種情況發(fā)生時,Objective-C只能用一個占位符對象來表示,而在Swift中就可以用一個系統(tǒng)類型來表示。
提供默認值
如果我們想當在Dictionary中查不到對應值的時候返回一個我們指定的默認值要怎么做呢?其實這也很簡單:
extension Dictionary {
func valuesForKeys(keys: [Key], notFoundMarker: Value) -> [Value] {
return self.valuesForKeys(keys).map { $0 ?? notFoundMarker }
}
}
dict.valuesForKeys(["B", "D"], notFoundMarker: "Anonymous")
// ["Bertha", "Anonymous"]
當Objective-C只能用占位符對象來做到這一點的時候,Swift卻可以使用系統(tǒng)類型來呈現(xiàn),并且提供了豐富的語法支持多樣化的返回結(jié)果。