Swift中Optional類型的使用案例分析:valuesForKeys

這篇文章將帶我們探索在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類型,這也就是說如果TOptional類型的話,那么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é)果。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容