Swift底層進(jìn)階--012:Optional

Optional

Optional的本質(zhì)是?個(gè)enum

Optional

當(dāng)前枚舉接收?個(gè)泛型參數(shù)说贝,有nonesome兩個(gè)case 作箍,?當(dāng)前some的關(guān)聯(lián)值是傳入的Wrapped

下?兩種寫法是完全等價(jià)的

var age: Int?
var age1: Optional<Int>

既然是枚舉东亦,也可以通過模式匹配來匹配對(duì)應(yīng)的值

var age: Int? = 10

switch age{
   case .none:
       print("age的值:nil")
   case .some(10):
       print("age的值:\(age)")
   default:
       print("age的值:unKonwn")
}

//輸出以下結(jié)果:
//age的值:Optional(10)
解包

涉及到Optional就要面臨解包的問題员辩。因?yàn)楫?dāng)前可選項(xiàng)是對(duì)值做了包裝频轿,如果當(dāng)前不為nil偷仿,需要從中拿到需要的值

強(qiáng)制解包
var age: Int? = 10
print(age!)

//輸出以下結(jié)果:
//10

使用強(qiáng)制解包,好處是省事衣屏,壞處是當(dāng)前agenil躏升,程序就會(huì)崩潰

程序崩潰

if let
var age: Int? = nil

if let unWrappedValue = age {
   print("value: \(unWrappedValue)")
}
else {
   print("age為nil")
}

//輸出以下結(jié)果:
//age為nil
  • 使?if let是通過可選項(xiàng)綁定的?式,判斷當(dāng)前可選項(xiàng)是否有值
  • 使?if let定義的unWrappedValue僅能在當(dāng)前if分?的?括號(hào)內(nèi)訪問
guard let
func test(_ age: Int?) {
   guard let unWrappedValue = age else {
       print("age為nil")
       return
   }

   print(unWrappedValue)
}

test(nil)

//輸出以下結(jié)果:
//age為nil
  • 使用guard let的判斷條件為false狼忱,才會(huì)執(zhí)??括號(hào)內(nèi)的代碼膨疏,反之執(zhí)?后?的代碼
  • 使用guard let定義的unWrappedValue在當(dāng)前?括號(hào)外部也能訪問
  • guard需要returnthrow配合使用,達(dá)到不符合條件時(shí)提前退出的目的

將上面代碼中的return注釋掉钻弄,編譯報(bào)錯(cuò)

編譯報(bào)錯(cuò)

Equatable

Swift中的類型佃却,可以通過遵循Equatable協(xié)議,使?相等運(yùn)算符==和不等運(yùn)算符!=窘俺,用來?較兩個(gè)值相等還是不相等饲帅。Swift標(biāo)準(zhǔn)庫(kù)中絕?多數(shù)的類型都默認(rèn)實(shí)現(xiàn)了Equatable協(xié)議

比如Int類型,系統(tǒng)默認(rèn)實(shí)現(xiàn)了==

var age1: Int = 10
var age2: Int = 20
var isEqual = age1 == age2

print(isEqual)

//輸出以下結(jié)果:
//false

Optional類型瘤泪,同樣遵循了Equatable協(xié)議灶泵,并重載了==運(yùn)算符

var age1: Int? = 10
var age2: Optional<Int> = Optional(10)
var isEqual = age1 == age2

print(isEqual)

//輸出以下結(jié)果:
//true

上述代碼,可以對(duì)Optional類型直接使用==判斷

遵循Equatable協(xié)議均芽,實(shí)現(xiàn)?定類型的==運(yùn)算符

struct LGTeacher: Equatable {
    var age: Int
    var name: String
}

var t1 = LGTeacher(age: 18, name: "Zang")
var t2 = LGTeacher(age: 18, name: "Zang")
var isEqual = t1 == t2

print(isEqual)

//輸出以下結(jié)果:
//true

通過打印可以看到丘逸,系統(tǒng)能夠正確判斷出t1t2兩個(gè)結(jié)構(gòu)體是否相等掀宋,也就意味著系統(tǒng)默認(rèn)實(shí)現(xiàn)==運(yùn)算符

將上述代碼生成SIL文件:swiftc -emit-sil main.swift | xcrun swift-demangle

__derived_struct_equals

bb0
bb1
bb4

如果是結(jié)構(gòu)體中嵌套結(jié)構(gòu)體深纲,內(nèi)嵌結(jié)構(gòu)體也要遵循Equatable協(xié)議,否則編譯報(bào)錯(cuò)

編譯報(bào)錯(cuò)

struct LGTeacher: Equatable {
    var age: Int
    var name: String
    var child: LGChild
}

struct LGChild: Equatable {
    var age: Int
    var name: String
}

var t1 = LGTeacher(age: 18, name: "Zang", child: LGChild(age: 10, name: "Child"))
var t2 = LGTeacher(age: 18, name: "Zang", child: LGChild(age: 10, name: "Child"))
var isEqual = t1 == t2

print(isEqual)

//輸出以下結(jié)果:
//true

上述代碼劲妙,只有LGTeacherLGChild都遵循Equatable協(xié)議湃鹊,才能使用==運(yùn)算符

如果是Class,除了遵循Equatable協(xié)議镣奋,還要自行實(shí)現(xiàn)func ==方法币呵,否則編譯報(bào)錯(cuò)

編譯報(bào)錯(cuò)

class LGTeacher: Equatable {
    var age: Int
    var name: String
    
    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
    
    static func == (lhs: LGTeacher, rhs: LGTeacher) -> Bool {
        return lhs.age == rhs.age && lhs.name == rhs.name
    }
}

var t1 = LGTeacher.init(age: 18, name: "Zang")
var t2 = LGTeacher.init(age: 18, name: "Zang")
var isEqual = t1 == t2

print(isEqual)

//輸出以下結(jié)果:
//true

上述代碼,Class必須自行實(shí)現(xiàn)func ==方法,才能使用==運(yùn)算符

==運(yùn)算符?來判斷值是否相等余赢,也就是equal to芯义。如果判斷兩個(gè)對(duì)象是否是同?個(gè)實(shí)例對(duì)象,需要使用===

class LGTeacher: Equatable {
    var age: Int
    var name: String
    
    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
    
    static func == (lhs: LGTeacher, rhs: LGTeacher) -> Bool {
        return lhs.age == rhs.age && lhs.name == rhs.name
    }
}

var t1 = LGTeacher.init(age: 18, name: "Zang")
var t2 = LGTeacher.init(age: 18, name: "Zang")

var isEqual1 = t1 == t2
var isEqual2 = t1 === t2

print(isEqual1)
print(isEqual2)

//輸出以下結(jié)果:
//true
//false
Comparable

Comparable自動(dòng)遵循Equatable協(xié)議妻柒,支持更多比較方式

public protocol Comparable : Equatable {
    static func < (lhs: Self, rhs: Self) -> Bool

    static func <= (lhs: Self, rhs: Self) -> Bool

    static func >= (lhs: Self, rhs: Self) -> Bool

    static func > (lhs: Self, rhs: Self) -> Bool
}

遵循Comparable協(xié)議扛拨,還要自行實(shí)現(xiàn)func <方法,否則編譯報(bào)錯(cuò)

編譯報(bào)錯(cuò)

遵循Comparable協(xié)議實(shí)現(xiàn)func <方法举塔,編譯器會(huì)通過<自動(dòng)實(shí)現(xiàn)其它運(yùn)算符

struct LGTeacher: Comparable {
    var age: Int
    var name: String

    static func < (lhs: LGTeacher, rhs: LGTeacher) -> Bool {
        return lhs.age < rhs.age
    }
}

var t1 = LGTeacher(age: 20, name: "Zang")
var t2 = LGTeacher(age: 18, name: "Zang")

var isEqual1 = t1 < t2
var isEqual2 = t1 > t2

print(isEqual1)
print(isEqual2)

//輸出以下結(jié)果:
//false
//true

上述代碼绑警,實(shí)現(xiàn)func <方法,<央渣、<=计盒、>=>等運(yùn)算符都能使用

空合運(yùn)算符(??)
var age: Int?
print("age:\(age1 ?? 10)")

//輸出以下結(jié)果:
//age:10

上述代碼芽丹,如果agenil北启,返回10

空合運(yùn)算符在源碼中有兩個(gè)方法,一個(gè)返回T拔第,一個(gè)返回T?

返回T

返回T?

這兩個(gè)方法的調(diào)用暖庄,跟空合運(yùn)算符后面的返回值類型有關(guān)

  • 如果返回的是非可選類型,調(diào)用上面返回T的方法
  • 如果是返回是可選類型楼肪,調(diào)用下面返回T?的方法
var age1: Int?
var age2: Int? = 20

var tmp = age1 ?? age2

print("tmp:\(tmp)")

//輸出以下結(jié)果:
//tmp:Optional(20)

上述代碼,age2是可選類型惹悄,返回的就是T?

空合運(yùn)算符后面返回值的類型春叫,還要跟當(dāng)前值的類型保持一致。當(dāng)前值是Int類型泣港,就不能返回String類型暂殖,否則編譯報(bào)錯(cuò)

編譯報(bào)錯(cuò)

可選鏈

可選鏈:允許在鏈上通過可選項(xiàng)去訪問屬性、方法

class LGTeacher {
    var name: String? = "LGTeacher"
    var child: LGChild?
}

class LGChild {
    var name: String? = "LGChild"
}

var t: LGTeacher? = LGTeacher()

if let name = t?.child?.name {
    print(name)
}

上述代碼当纱,無論tchild哪個(gè)值為nil呛每,后面的鏈?zhǔn)秸{(diào)用都不會(huì)執(zhí)行,直接返回nil

class LGTeacher {
    var name: String? = "LGTeacher"
    var child: LGChild?
}

class LGChild {
    var name: String? = "LGChild"
    
    func test() {
        print("test")
    }
}

var t: LGTeacher? = LGTeacher()
t?.child?.test()

上述代碼坡氯,對(duì)于方法也是一樣的晨横,無論tchild哪個(gè)值為nil,后面的test方法都不會(huì)被調(diào)用

unsafelyUnwrapped

unsafelyUnwrapped和強(qiáng)制解包的作用一致箫柳,但Release模式下又有一些區(qū)別

var age: Int? = 30

print(age!)
print(age.unsafelyUnwrapped)

//輸出以下結(jié)果:
//30
//30

上述代碼手形,使用age!age.unsafelyUnwrapped效果完全一樣,打印結(jié)果都是30

如果age的值是nil悯恍,使用age.unsafelyUnwrapped訪問库糠,同樣是程序崩潰

程序崩潰

unsafelyUnwrapped和強(qiáng)制解包在Release模式下的一些區(qū)別

Release模式

使用age!強(qiáng)制解包,程序崩潰

age!

使用age.unsafelyUnwrapped涮毫,程序沒有崩潰瞬欧,打印結(jié)果0

age.unsafelyUnwrapped

as

as用于類型轉(zhuǎn)換贷屎,可以使用asas?艘虎、as!幾種方式

as

var age: Int = 10
var age1 = age as Any
print(age1)

//輸出以下結(jié)果:
//10

使用asInt類型轉(zhuǎn)換為Any唉侄、AnyObject都沒問題。但轉(zhuǎn)換為Double類型顷帖,編譯報(bào)錯(cuò)

編譯報(bào)錯(cuò)

as?

var age: Int = 10
var age1 = age as? Double
print(age1)

//輸出以下結(jié)果:
//nil

as?可以避免編譯報(bào)錯(cuò)美旧,使用as?返回的是可選項(xiàng),如果轉(zhuǎn)換失敗贬墩,直接返回nil

返回可選項(xiàng)

as!

var age: Int = 10
var age1 = age as! Double
print(age)

使用as!強(qiáng)制類型轉(zhuǎn)換榴嗅,如果轉(zhuǎn)換失敗,程序直接崩潰

程序崩潰

訪問控制

OC中很少接觸這個(gè)概念陶舞,但Swift中針對(duì)源?件和模塊的代碼嗽测,提供不同程度的訪問控制

  • 訪問級(jí)別由低到高:private<filePrivate<internal<public<open
  • 不指定訪問級(jí)別,默認(rèn)都是internal
private

訪問級(jí)別僅在當(dāng)前定義的作?域內(nèi)有效

案例1:在LGTeacher中定義了?個(gè)private變量肿孵,這時(shí)當(dāng)前變量的訪問控制權(quán)限唠粥,僅在這個(gè)類定義的作?域內(nèi)有效。如果在當(dāng)前作?域之外訪問停做,編譯報(bào)錯(cuò)

編譯報(bào)錯(cuò)

案例2:Swift單例的正確寫法就是通過private控制訪問權(quán)限晤愧,限制當(dāng)前init?法,僅在這個(gè)類定義的作?域內(nèi)有效

class LGTeacher{
  static let sha = LGTeacher()
  private init(){}
}

var t = LGTeacher.sha
filePrivate

此訪問限制僅限制在當(dāng)前定義的源?件中

案例1:在access.swift中定義LGTeacher類蛉腌,訪問權(quán)限設(shè)置為fileprivate官份。將全局變量t聲明為LGTeacher類型,編譯報(bào)錯(cuò)

編譯報(bào)錯(cuò)

案例1的問題:變量t屬于全局變量烙丛,訪問權(quán)限是internal舅巷,變量訪問權(quán)限高于類型訪問權(quán)限,所以編譯報(bào)錯(cuò)

fileprivate class LGTeacher{
   var age: Int = 10
}

fileprivate var t1: LGTeacher = LGTeacher()
private var t2: LGTeacher = LGTeacher()

上述代碼河咽,需要將變量訪問權(quán)限設(shè)置為filePrivate钠右,或級(jí)別更低的private,才能聲明成功

案例2:在access.swift中定義LGTeacher類忘蟹,將age屬性設(shè)置為filePrivate

//  access.swift
class LGTeacher{
   fileprivate var age: Int = 10
}

main.swift中訪問age屬性飒房,編譯報(bào)錯(cuò)

編譯報(bào)錯(cuò)

internal

默認(rèn)訪問級(jí)別,允許定義模塊中的任意源?件訪問寒瓦,但不能被該模塊之外的任何源?件訪問

模塊是指框架或應(yīng)用程序情屹,使用import導(dǎo)入的框架都是模塊,例如Foundation

import Foundation
public

開放式訪問

  • 允許任意模塊杂腰、任意源?件的訪問
  • 只能在定義的模塊中繼承和?類重寫
open

最不受限制的訪問級(jí)別

  • 允許任意模塊垃你、任意源?件的訪問
  • 允許任意模塊中繼承和?類重寫
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子惜颇,更是在濱河造成了極大的恐慌皆刺,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凌摄,死亡現(xiàn)場(chǎng)離奇詭異羡蛾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)锨亏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門痴怨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人器予,你說我怎么就攤上這事浪藻。” “怎么了乾翔?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵爱葵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我反浓,道長(zhǎng)萌丈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任雷则,我火速辦了婚禮辆雾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘月劈。我一直安慰自己乾颁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布艺栈。 她就那樣靜靜地躺著,像睡著了一般湾盒。 火紅的嫁衣襯著肌膚如雪湿右。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天罚勾,我揣著相機(jī)與錄音毅人,去河邊找鬼。 笑死尖殃,一個(gè)胖子當(dāng)著我的面吹牛丈莺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播送丰,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼缔俄,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起俐载,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤蟹略,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后遏佣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挖炬,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年状婶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了意敛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡膛虫,死狀恐怖草姻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情走敌,我是刑警寧澤碴倾,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站掉丽,受9級(jí)特大地震影響跌榔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜捶障,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一僧须、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦篮赢、人聲如沸救崔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拌禾,卻和暖如春取胎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背湃窍。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工闻蛀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人您市。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓觉痛,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親茵休。 傳聞我的和親對(duì)象是個(gè)殘疾皇子薪棒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

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

  • 在Optional.Swift源碼中手蝎,Optional定義如下。 Optional是通過enum實(shí)現(xiàn)的盗尸,Optio...
    HotPotCat閱讀 1,623評(píng)論 1 2
  • swift社區(qū):http://www.swift51.com[http://www.swift51.com](開源...
    白水灬煮一切閱讀 1,965評(píng)論 6 6
  • 小視頻 001--swift簡(jiǎn)史小視頻 002--Playground體驗(yàn) 003--常量&變量 一柑船、swift簡(jiǎn)...
    謙謙君子修羅刀閱讀 6,950評(píng)論 8 66
  • 136.泛型 泛型代碼讓你可以寫出靈活,可重用的函數(shù)和類型,它們可以使用任何類型,受你定義的需求的約束。你可以寫出...
    無灃閱讀 1,476評(píng)論 0 4
  • 推薦指數(shù): 6.0 書籍主旨關(guān)鍵詞:特權(quán)泼各、焦點(diǎn)鞍时、注意力、語言聯(lián)想扣蜻、情景聯(lián)想 觀點(diǎn): 1.統(tǒng)計(jì)學(xué)現(xiàn)在叫數(shù)據(jù)分析逆巍,社會(huì)...
    Jenaral閱讀 5,726評(píng)論 0 5