Swift3.0 - 可選類型

在編程世界中有一種非常通用的模式,那就是某個(gè)操作是否要返回一個(gè)有效值。

Objective-C 中挟憔,對(duì) nil 發(fā)送消息是安全的。如果這個(gè)消息簽名返回一個(gè)對(duì)象烟号,那么
nil 會(huì)被返回绊谭;如果消息返回的是一個(gè)結(jié)構(gòu)體那么它的值都將為零。但在實(shí)際開發(fā)中我們常常會(huì)碰到由于對(duì)象為 nil 而導(dǎo)致的崩潰或者不想出現(xiàn)的問題汪拥,比如數(shù)組达传、字典、block的判空處理等等喷楣。

Tony Hoare在1965年設(shè)計(jì)了 null 引用趟大,他對(duì)此設(shè)計(jì)表示痛心疾首,并將這個(gè)問題稱為“價(jià)值10億美元的錯(cuò)誤”:

那時(shí)候铣焊,我正在為一門面向?qū)ο蟮恼Z言(ALGOL W)設(shè)計(jì)第一個(gè)全面的引用類型系統(tǒng)逊朽。我的目標(biāo)是在編譯器自動(dòng)執(zhí)行的檢查的保證下,確保對(duì)于引用的所有使用都是安全的曲伊。但是我沒能抵擋住 null 引用的誘惑叽讳,因?yàn)樗菀讓?shí)現(xiàn)了追他。這導(dǎo)致了不計(jì)其數(shù)的錯(cuò)誤,漏洞以及系統(tǒng)崩潰岛蚤。這個(gè)問題可能在過去四十年里造成了有十億美元的損失邑狸。

前面敘述這么多是為了引出編程語言中更安全的設(shè)計(jì) - swift中的可選類型

swift定義后綴 ? 來作為標(biāo)準(zhǔn)庫中的定義的命名型類型 Optional<Wrapped>語法糖。換句話說涤妒,下面兩個(gè) 聲明是等價(jià)的:

var optionalInteger: Int?
var optionalInteger: Optional<Int>

在上述兩種情況下单雾,變量 optionalInteger 都被聲明為可選整型類型。注意在類型和 ? 之間沒有空格她紫。

類型 Optional<Wrapped> 是一個(gè)枚舉硅堆,有兩個(gè)成員,nonesome(Wrapped)贿讹,用來表示可能有也可能沒有的值渐逃。任意類型都可以被顯式地聲明(或隱式地轉(zhuǎn)換)為可選類型。如果你在聲明或定義可選變量或?qū)傩缘臅r(shí)候沒有提供初始值民褂,它的值則會(huì)自動(dòng)賦為默認(rèn)值 nil 茄菊。

如果一個(gè)可選類型的實(shí)例包含一個(gè)值,那么你就可以使用后綴運(yùn)算符 ! 來獲取該值(即強(qiáng)制解包)赊堪,正如下面描述的:

optionalInteger = 42
optionalInteger! // 42

使用 ! 運(yùn)算符解包值為 nil 的可選值會(huì)導(dǎo)致運(yùn)行錯(cuò)誤面殖。

你也可以使用可選鏈?zhǔn)秸{(diào)用和可選綁定來選擇性地在可選表達(dá)式上執(zhí)行操作。如果值為 nil 雹食,不會(huì)執(zhí)行任何操作畜普,因此也就沒有運(yùn)行錯(cuò)誤產(chǎn)生。

  • 處理可選值值缺失的方法

一個(gè)可選的值是一個(gè)具體的值或者是 nil 以表示值缺失群叶。在類型后面加一個(gè)問號(hào)來標(biāo)記這個(gè)變量的值是可選的吃挑。你可以一起使用 iflet來處理值缺失的情況:

var optionalString: String? = "Hello"
print(optionalString == nil)  //false

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, \(name)"
}

上面的代碼中如果變量optionalName的可選值是 nil,條件會(huì)判斷為 false,大括號(hào)中的代碼會(huì)被跳過街立。如果不是 nil舶衬,會(huì)將值解包并賦給 let 后面的常量 name,這樣代碼塊中就可以使用這個(gè)值了赎离。

還有另外一種處理值缺失的方法逛犹,使用 ?? 操作符來提供一個(gè)默認(rèn)值。如果可選值缺失的話梁剔,可以使用默認(rèn)值來代替虽画。

 let nickName: String? = nil
 let fullName: String = "John Appleseed"
 let informalGreeting = "Hi \(nickName ?? fullName)"

因?yàn)?nickNamenil,所以 informalGreeting的值就變成了 "Hi John Appleseed"荣病。

  • 隱式解析可選類型

當(dāng)可以被訪問時(shí)码撰,Swift 語言定義后綴 ! 作為標(biāo)準(zhǔn)庫中命名類型 Optional<Wrapped>的語法糖,來實(shí)現(xiàn)自動(dòng) 解包的功能个盆。換句話說脖岛,下面兩個(gè)聲明等價(jià):

var implicitlyUnwrappedString: String!
var explicitlyUnwrappedString: Optional<String>

由于隱式解包修改了包涵其類型的聲明語義朵栖,嵌套在元組類型或泛型的可選類型(比如字典元素類型或數(shù)組元素
類型),不能被標(biāo)記為隱式解包柴梆。例如:

let tupleOfImplicitlyUnwrappedElements: (Int!, Int!) // 錯(cuò)誤
let implicitlyUnwrappedTuple: (Int, Int)! // 正確
let arrayOfImplicitlyUnwrappedElements: [Int!] // 錯(cuò)誤 
let implicitlyUnwrappedArray: [Int]! // 正確

由于隱式解析可選類型和可選類型有同樣的表達(dá)式析可選類型陨溅。比如,你可以將隱式解析可選類型的值賦給變量绍在、常量和可選屬性门扇,反之亦然。

正如可選類型一樣揣苏,你在聲明隱式解析可選類型的變量或?qū)傩缘臅r(shí)候也不用指定初始值悯嗓,因?yàn)樗心J(rèn)值 nil
可以使用可選鏈?zhǔn)秸{(diào)用來在隱式解析可選表達(dá)式上選擇性地執(zhí)行操作卸察。如果值為 nil ,就不會(huì)執(zhí)行任何操作铅祸,因此也不會(huì)產(chǎn)生運(yùn)行錯(cuò)誤坑质。

  • 使用可選鏈?zhǔn)秸{(diào)用代替強(qiáng)制展開

通過在想調(diào)用的屬性、方法临梗、或下標(biāo)的可選值后面放一個(gè)問號(hào) ? 涡扼,可以定義一個(gè)可選鏈。這一點(diǎn)很像在可選 值后面放一個(gè)嘆號(hào) ! 來強(qiáng)制展開它的值盟庞。它們的主要區(qū)別在于當(dāng)可選值為空時(shí)可選鏈?zhǔn)秸{(diào)用只會(huì)調(diào)用失敗吃沪,然而強(qiáng)制展開將會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤。

為了反映可選鏈?zhǔn)秸{(diào)用可以在空值 nil 上調(diào)用的事實(shí)什猖,不論這個(gè)調(diào)用的屬性票彪、方法及下標(biāo)返回的值是不是可選值,它的返回結(jié)果都是一個(gè)可選值不狮。你可以利用這個(gè)返回值來判斷你的可選鏈?zhǔn)秸{(diào)用是否調(diào)用成功降铸,如果調(diào)用 有返回值則說明調(diào)用成功,返回 nil 則說明調(diào)用失敗摇零。

特別地推掸,可選鏈?zhǔn)秸{(diào)用的返回結(jié)果與原本的返回結(jié)果具有相同的類型,但是被包裝成了一個(gè)可選值驻仅。例如谅畅,使用 可選鏈?zhǔn)秸{(diào)用訪問屬性,當(dāng)可選鏈?zhǔn)秸{(diào)用成功時(shí)噪服,如果屬性原本的返回結(jié)果是 Int 類型毡泻,則會(huì)變?yōu)?Int? 類型。

代碼示例芯咧,首先定義兩個(gè)類 PersonResidence :

 class Person {
     var residence: Residence?
}
 class Residence {
     var numberOfRooms = 1
}

Residence 有一個(gè) Int 類型的屬性 numberOfRooms 牙捉,其默認(rèn)值為 1 竹揍。 Person 具有一個(gè)可選的 residence 屬性,其類型為 Residence? 邪铲。

假如你創(chuàng)建了一個(gè)新的 Person 實(shí)例,它的 residence 屬性由于是是可選型而將初始化為 nil ,在下面的代碼中, john 有一個(gè)值為 nilresidence 屬性:

 let john = Person()

如果使用嘆號(hào) ! 強(qiáng)制展開獲得這個(gè) johnresidence 屬性中的 numberOfRooms 值芬位,會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤,因?yàn)檫@時(shí) residence 沒有可以展開的值:

let roomCount = john.residence!.numberOfRooms // 這會(huì)引發(fā)運(yùn)行時(shí)錯(cuò)誤

john.residence 為非 nil 值的時(shí)候带到,上面的調(diào)用會(huì)成功昧碉,并且把 roomCount 設(shè)置為 Int 類型的房間數(shù)量。正如上面提到的揽惹,當(dāng) residencenil 的時(shí)候上面這段代碼會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤被饿。

可選鏈?zhǔn)秸{(diào)用提供了另一種訪問 numberOfRooms 的方式,使用問號(hào) ? 來替代原來的嘆號(hào) ! :

if let roomCount = john.residence?.numberOfRooms {
     print("John's residence has \(roomCount) room(s).")
 } else {
     print("Unable to retrieve the number of rooms.")
}
// 打印 “Unable to retrieve the number of rooms.”

residence 后面添加問號(hào)之后搪搏,Swift 就會(huì)在 residence 不為 nil 的情況下訪問 numberOfRooms 狭握。

因?yàn)樵L問 numberOfRooms 有可能失敗,可選鏈?zhǔn)秸{(diào)用會(huì)返回 Int? 類型疯溺,或稱為“可選的 Int ”论颅。如上例所 示,當(dāng) residencenil 的時(shí)候囱嫩,可選的 Int 將會(huì)為 nil 恃疯,表明無法訪問 numberOfRooms 。訪問成功時(shí)墨闲,可選的
Int 值會(huì)通過可選綁定展開今妄,并賦值給非可選類型的 roomCount 常量。

要注意的是鸳碧,即使 numberOfRooms 是非可選的 Int 時(shí)盾鳞,這一點(diǎn)也成立。只要使用可選鏈?zhǔn)秸{(diào)用就意味著 numberOfRooms 會(huì)返回一個(gè) Int? 而不是 Int 杆兵。

可以將一個(gè) Residence 的實(shí)例賦給 john.residence 雁仲,這樣它就不再是 nil 了:

john.residence = Residence()

john.residence 現(xiàn)在包含一個(gè)實(shí)際的 Residence 實(shí)例,而不再是 nil 琐脏。如果你試圖使用先前的可選鏈?zhǔn)秸{(diào)用訪問 numberOfRooms 攒砖,它現(xiàn)在將返回值為 1 的 Int? 類型的值:

if let roomCount = john.residence?.numberOfRooms {
     print("John's residence has \(roomCount) room(s).")
 } else {
     print("Unable to retrieve the number of rooms.")
}
// 打印 “John's residence has 1 room(s).”
  • 強(qiáng)制解包的時(shí)機(jī)

當(dāng)你能確定你的某個(gè)值不可能是 nil 時(shí)使用 ! 進(jìn)行強(qiáng)制解包,你應(yīng)當(dāng)不希望如果它不巧意外地是 nil 的話日裙,這句程序會(huì)直接掛掉吹艇。

總之,使用強(qiáng)制解包時(shí)一定要慎重0悍鳌J苌瘛!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末格侯,一起剝皮案震驚了整個(gè)濱河市鼻听,隨后出現(xiàn)的幾起案子财著,更是在濱河造成了極大的恐慌,老刑警劉巖撑碴,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撑教,死亡現(xiàn)場離奇詭異,居然都是意外死亡醉拓,警方通過查閱死者的電腦和手機(jī)伟姐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來亿卤,“玉大人愤兵,你說我怎么就攤上這事∨盼猓” “怎么了秆乳?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長傍念。 經(jīng)常有香客問我矫夷,道長,這世上最難降的妖魔是什么憋槐? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮淑趾,結(jié)果婚禮上阳仔,老公的妹妹穿的比我還像新娘。我一直安慰自己扣泊,他們只是感情好近范,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著延蟹,像睡著了一般评矩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上阱飘,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天斥杜,我揣著相機(jī)與錄音,去河邊找鬼沥匈。 笑死蔗喂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的高帖。 我是一名探鬼主播缰儿,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼散址!你這毒婦竟也來了乖阵?” 一聲冷哼從身側(cè)響起宣赔,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瞪浸,沒想到半個(gè)月后儒将,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡默终,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年椅棺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片齐蔽。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡两疚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出含滴,到底是詐尸還是另有隱情诱渤,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布谈况,位于F島的核電站勺美,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏碑韵。R本人自食惡果不足惜赡茸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望祝闻。 院中可真熱鬧占卧,春花似錦、人聲如沸联喘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽豁遭。三九已至叭喜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蓖谢,已是汗流浹背捂蕴。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蜈抓,地道東北人启绰。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像沟使,于是被迫代替她去往敵國和親委可。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 本章將會(huì)介紹 自動(dòng)引用計(jì)數(shù)的工作機(jī)制自動(dòng)引用計(jì)數(shù)實(shí)踐類實(shí)例之間的循環(huán)強(qiáng)引用解決實(shí)例之間的循環(huán)強(qiáng)引用閉包引起的循環(huán)強(qiáng)...
    寒橋閱讀 900評(píng)論 0 0
  • 可選鏈?zhǔn)秸{(diào)用 是一種可以在當(dāng)前值可能為 nil 的可選值上請(qǐng)求和調(diào)用屬性卡者、方法及下標(biāo)的方法蒿囤。如果可選值有值,那么調(diào)...
    莽原奔馬668閱讀 325評(píng)論 0 2
  • 可選類型 使用可選類型(optionals)來處理值可能缺失的情況崇决〔姆蹋可選類型表示: 有值,等于 x 或者 沒有值,...
    蠱毒_閱讀 418評(píng)論 0 1
  • 126.析構(gòu)器 在一個(gè)類實(shí)例銷毀前,一個(gè)析構(gòu)器會(huì)立即調(diào)用恒傻。使用deinit 關(guān)鍵字來表示析構(gòu)器, 跟構(gòu)造器寫法類似...
    無灃閱讀 798評(píng)論 0 4
  • 夜已經(jīng)深了脸侥,卻沒有睡意,可能因?yàn)殡娪爸心莻€(gè)男孩的死盈厘,可能是因?yàn)樯贁?shù)人的聲音不被聽到的那種可悲和絕望睁枕,還好最后那群男...
    玖月的喵閱讀 59評(píng)論 0 2