鍍金池/ 教程/ iOS/ 可選類型完美解決占位問(wèn)題
特性(Attributes)
Access Control 權(quán)限控制的黑與白
基本運(yùn)算符(Basic Operators)
基礎(chǔ)部分(The Basics)
閉包(Closures)
擴(kuò)展
泛型參數(shù)(Generic Parameters and Arguments)
訪問(wèn)控制和 protected
語(yǔ)句(Statements)
模式(Patterns)
WWDC 里面的那個(gè)“大炮打氣球”
關(guān)于語(yǔ)言參考(About the Language Reference)
語(yǔ)法總結(jié)(Summary of the Grammar)
嵌套類型
類型(Types)
Swift 初見(jiàn)(A Swift Tour)
泛型
枚舉(Enumerations)
高級(jí)運(yùn)算符
繼承
析構(gòu)過(guò)程
關(guān)于 Swift(About Swift)
訪問(wèn)控制
類和結(jié)構(gòu)體
內(nèi)存安全
Swift 與 C 語(yǔ)言指針友好合作
協(xié)議
屬性(Properties)
可選類型完美解決占位問(wèn)題
錯(cuò)誤處理
字符串和字符(Strings and Characters)
聲明(Declarations)
自動(dòng)引用計(jì)數(shù)
Swift 里的值類型與引用類型
表達(dá)式(Expressions)
Swift 文檔修訂歷史
造個(gè)類型不是夢(mèng)-白話 Swift 類型創(chuàng)建
歡迎使用 Swift
詞法結(jié)構(gòu)(Lexical Structure)
集合類型(Collection Types)
下標(biāo)
方法(Methods)
可選鏈?zhǔn)秸{(diào)用
版本兼容性
類型轉(zhuǎn)換
構(gòu)造過(guò)程
The Swift Programming Language 中文版
函數(shù)(Functions)
Swift 教程
控制流(Control Flow)

可選類型完美解決占位問(wèn)題


翻譯:老碼團(tuán)隊(duì)翻譯組-Tyrion 校對(duì):老碼團(tuán)隊(duì)翻譯組-Ayra

本頁(yè)包含內(nèi)容:

可選類型是 Swift 中新引入的,功能很強(qiáng)大。在這篇博文里討論的,是在 Swift 里,如何通過(guò)可選類型來(lái)保證強(qiáng)類型的安全性。作為例子,我們來(lái)創(chuàng)建一個(gè) Objective-C API 的 Swift 版本,但實(shí)際上 Swift 本身并不需要這樣的 API。

為 Dictionary 增加 objectsForKeys 函數(shù)

在 Objective-C 中,NSDictionary 有一個(gè)方法 -objectsForKeys:NoFoundMarker:, 這個(gè)方法需要一個(gè) NSArray 數(shù)組作為鍵值參數(shù),然后返回一個(gè)包含相關(guān)值的數(shù)組。文檔里寫(xiě)到:“返回?cái)?shù)組中的第 N 個(gè)值,和輸入數(shù)組中的第 N 個(gè)值相對(duì)應(yīng)”,那如果有某個(gè)鍵值在字典里不存在呢?于是就有了 notFoundMarker 作為返回提示。比如第三個(gè)鍵值沒(méi)有找到,那么在返回?cái)?shù)組中第三個(gè)值就是這個(gè) notFoundMarker,而不是字典中的第三個(gè)值,但是這個(gè)值只是用來(lái)提醒原字典中沒(méi)有找到對(duì)應(yīng)值,但在返回?cái)?shù)組中該元素存在,且用 notFoundMarker 作為占位符,因?yàn)檫@個(gè)對(duì)象不能直接使用,所以在 Foundation 框架中有個(gè)專門(mén)的類處理這個(gè)情況:NSNull。

在 Swift 中,Dictionary 類沒(méi)有類似 objectsForKeys 的函數(shù),為了說(shuō)明問(wèn)題,我們動(dòng)手加一個(gè),并且使其成為操作字典值的通用方法。我們可以用 extension 來(lái)實(shí)現(xiàn):

extension Dictionary{
    func valuesForKeys(keys:[K], notFoundMarker: V )->[V]{
        //具體實(shí)現(xiàn)代碼后面會(huì)寫(xiě)到
    }
}

以上就是我們實(shí)現(xiàn)的 Swift 版本,這個(gè)和 Objective-C 版本有很大區(qū)別。在 Swift 中,因?yàn)槠鋸?qiáng)類型的原因限制了返回的結(jié)果數(shù)組只能包含單一類型的元素,所以我們不能放 NSNull 在字符串?dāng)?shù)組中,但是,Swift 有更好的選擇,我們可以返回一個(gè)可選類型數(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 中更簡(jiǎn)便的方法

小伙伴們可能會(huì)問(wèn),為什么 Swift 中不需要實(shí)現(xiàn)這么一個(gè) API 呢?其實(shí)其有更簡(jiǎn)單的實(shí)現(xiàn),如下面代碼所示:

extension Dictionary {
    func valuesForKeys(keys: [Key]) -> [Value?] {
        return keys.map { self[$0] }
    }
}

上述方式實(shí)現(xiàn)的功能和最開(kāi)始的方法實(shí)現(xiàn)的功能相同,雖然核心的功能是封裝了 map 的調(diào)用,這個(gè)例子也說(shuō)明了為什么 Swift 沒(méi)有提供輕量級(jí)的 API 接口,因?yàn)樾』锇閭兒?jiǎn)單的調(diào)用 map 就可以實(shí)現(xiàn)。

接下來(lái),我們實(shí)驗(yàn)幾個(gè)例子:

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é)果為:[]

內(nèi)嵌可選類型

現(xiàn)在,如果我們?yōu)槊恳粋€(gè)結(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

小伙伴們立馬迷糊了,為什么會(huì)出現(xiàn)兩層包含的可選類型呢?,特別對(duì)第二種情況的 Optional(nil),這是什么節(jié)奏?

我們回過(guò)頭看看 last 屬性的定義:

var last:T? { get }

很明顯 last 屬性的類型是數(shù)組元素類型的可選類型,這種情況下,因?yàn)樵仡愋褪?(String?),那么再結(jié)合返回的類型,于是其結(jié)果就是 String?? 了,這就是所謂的嵌套可選類型。但嵌套可選類型本質(zhì)是什么意思呢?

如果在 Objective-C 中重新調(diào)用上述方法,我們將使用 NSNull 作為占位符,Objective-C 的調(diào)用語(yǔ)法如下所示:

[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ù)組是空的,所以它就沒(méi)有最后一個(gè)元素。 但是如果返回是 Optional(nil) 或者 Objective-C 中的 NSNull 都表示數(shù)組中的最后一個(gè)元素存在,但是元素的內(nèi)容是空的。在 Objective-C 中只能借助 NSNull 作為占位符來(lái)達(dá)到這個(gè)目的,但是 Swift 卻可以語(yǔ)言系統(tǒng)類型的角度的實(shí)現(xiàn)。

提供一個(gè)默認(rèn)值

進(jìn)一步封裝,如果我字典中的某個(gè)或某些元素不存在,我們想提供一個(gè)默認(rèn)值怎么辦呢?實(shí)現(xiàn)方法很簡(jiǎ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 相比,其需要占位符來(lái)達(dá)到占位的目的,但是 Swift 卻已經(jīng)從語(yǔ)言類型系統(tǒng)的層面原生的支持了這種用法,同時(shí)提供了豐富的語(yǔ)法功能。這就是 Swift 可選類型的強(qiáng)大之處。同時(shí)注意上述例子中用到了空合運(yùn)算符 ??。


本章節(jié)不是老碼的原創(chuàng),是老碼認(rèn)真的閱讀了蘋(píng)果的官方博客,自己的練習(xí)總結(jié),如果小伙伴們費(fèi)了吃奶的勁還是看不懂,請(qǐng)找度娘谷歌。還是看不懂?請(qǐng)到老碼官方微博咆哮。

本文由翻譯自 Apple Swift Blog :Optionals Case Study: valuesForKeys
上一篇:控制流(Control Flow)下一篇:繼承