從零學(xué)習(xí)Swift 10: 可選鏈,協(xié)議,元類型

總結(jié)
一: 可選鏈

之前寫的代碼都是這樣調(diào)用:

如果person是可選類型,就會(huì)報(bào)錯(cuò):

解決這種報(bào)錯(cuò)有兩種方式,一種是在person后面加感嘆號(hào)!,進(jìn)行強(qiáng)制解包:

使用感嘆號(hào)!強(qiáng)制解包這種做法不安全,一旦person對(duì)象為nil,程序就會(huì)崩潰.

另一種是加問號(hào)?:

?的意思時(shí)會(huì)先判斷person對(duì)象是否為nil,如果為nil,就不會(huì)再調(diào)用age,直接返回nil;如果person對(duì)象不為nil,才會(huì)調(diào)用age,正常返回;那什么類型既能接受nil,又能接受Int呢?當(dāng)然是可選項(xiàng)類型.

變量age是可選項(xiàng)類型

所以,只要通過person?調(diào)用的屬性,方法,下標(biāo)等等,它們的結(jié)果要么是nil,要么就被包裝成可選項(xiàng)類型:


class Dog{
    var name: String = "大黃"
}

class Cat{
    var color: String = "橘色"
}

class Person{
    var name: String = ""
    var age: Int = 0
    var dog: Dog = Dog()
    var cat: Cat? = Cat()
    func eat(){
    print("eat")
    }
    
    func height() -> Int{
        180
    }
    
    subscript(index: Int) -> Int{
        index
    }
    
}

var person: Person? = nil
var name = person?.name //String?
var age = person?.age//Int?
var color = person?.cat?.color //String?
var height = person?.height()// Int?
var index = person?[3] // Int?

上面代碼person?.cat?.color使用多個(gè)?鏈接在一起就被稱為可選鏈.如果可選鏈的任何一個(gè)節(jié)點(diǎn)為nil,整個(gè)調(diào)用就會(huì)失敗.

所以我們拿到調(diào)用結(jié)果使用的時(shí)候就要解包了,一般用可選項(xiàng)綁定判斷一下:


if let a = age{
    print("age 的值為",a)
}else{
    print("age 為nil")
}

同樣也可以判斷方法調(diào)用是否成功:


if let result = person?.eat(){
    print("調(diào)用成功",result)
}else{
    print("調(diào)用失敗")
}

eat()方法并沒有返回值,為什么也可以用可選項(xiàng)綁定來判斷呢.因?yàn)闊o返回值就是Void,而Void的本質(zhì)就是空元組.所以如果eat()調(diào)用成功就會(huì)返回一個(gè)空元組,調(diào)用失敗就返回nil.所以可以使用可選項(xiàng)綁定判斷.

思考一下下面代碼的打印結(jié)果:


var a: Int? = 10
a? = 5
print("a",a)

var b: Int? = nil
b? = 5
print("b",b)


打印結(jié)果為a Optional(5), b nil;因?yàn)?code>b?會(huì)先判斷b是否為nil,如果為nil就不會(huì)執(zhí)行= 5的賦值運(yùn)算.

二: 協(xié)議(Protocol)

swift 中的協(xié)議可以定義屬性 , 方法 , 下標(biāo)的聲明,協(xié)議可以被結(jié)構(gòu)體,枚舉,類遵守.

我們?cè)诙x協(xié)議時(shí)要遵守以下規(guī)則:

  1. 協(xié)議定義屬性時(shí)只能用 var , 不能用 let
協(xié)議中的屬性不能使用 let
  1. 實(shí)現(xiàn)協(xié)議時(shí)的屬性權(quán)限要不小于協(xié)議中定義的屬性權(quán)限
實(shí)現(xiàn)協(xié)議中的屬性時(shí),權(quán)限小于協(xié)議中定義的權(quán)限

如上圖所示,協(xié)議中定義的slogan屬性權(quán)限是set , get.而Person實(shí)現(xiàn)協(xié)議時(shí)的屬性權(quán)限只有get,就會(huì)報(bào)錯(cuò).

3.協(xié)議中的類型屬性,類型方法,類型下標(biāo)只能用 static 定義,不能使用 class
我們知道classstatic都可以用來定義類型計(jì)算屬性,類型方法,類型下標(biāo).但是如果要在協(xié)議中定義類型方法,類型屬性只能用static.因?yàn)閰f(xié)議可以被類,結(jié)構(gòu)體,枚舉遵守.而class只能在類中使用.

  1. 如果要在值類型(結(jié)構(gòu)體 , 枚舉)實(shí)現(xiàn)的協(xié)議方法中修改自身內(nèi)存,必須在定義協(xié)議方法前加上mutating關(guān)鍵字.不能在實(shí)現(xiàn)協(xié)議方法時(shí)添加,那樣無效.
  1. 協(xié)議中定義的初始化器init,非final類實(shí)現(xiàn)協(xié)議時(shí)必須加上required.
    這樣做是為了保證所有的子類都有此初始化器.

協(xié)議的繼承
協(xié)議之間可以彼此繼承,一個(gè)協(xié)議可以繼承另一個(gè)協(xié)議.

協(xié)議之間的繼承

協(xié)議的組合
可以通過不同的協(xié)議組合來規(guī)范參數(shù)類型或者返回值類型,也可以和類組合使用,但最多組合一個(gè)類


protocol Study {
    func read()
}

protocol Sport{
    func run()
}

class Person: Sport{
    func run() {
        
    }
    
    func read() {
        
    }
}


func test1(obj: Study){
    //接受遵守Study協(xié)議的實(shí)例
}

func test2(obj: Study & Sport){
    //接受遵守Study 和 Sport協(xié)議的實(shí)例
}

func test3(obj: Person & Study & Sport){
    //接受遵守Study 和 Sport協(xié)議,并且是Person或者其子類的實(shí)例
}


注意: 只能用 & 組合協(xié)議,不能使用 | 組合協(xié)議,因?yàn)槭褂?| 組合還要單獨(dú)區(qū)分具體是哪個(gè)類,這樣做毫無意義.

Any , AnyObject

Swift 中提供了兩種特殊的類型Any,AnyObject
Any:可以代表任意類型(枚舉, 結(jié)構(gòu)體,類,函數(shù)類型)
AnyObject:代表任意類類型(也就是使用class創(chuàng)建的任意類型)

例如創(chuàng)建一個(gè)可以添加任意類型的數(shù)組:

//添加任意類型的數(shù)組
var array = Array<Any>()

array.append(1) // Int
array.append("2")// 字符串
array.append(Person())// 類對(duì)象
array.append(Season.spring) // 枚舉

創(chuàng)建數(shù)組還有另一種寫法:


var array2 = [Any]()
array2.append("hello")

注意: 如果在協(xié)議后面加上 AnyObject 或者 class , 代表只有類類型才能遵守協(xié)議

is , as , as? , as!
is: 判斷是否為某種類型
as: 用來做強(qiáng)制轉(zhuǎn)換

is 類型判斷
as? 類型轉(zhuǎn)換

如果使用as!強(qiáng)制解包,沒有值的情況下會(huì)直接崩潰,所以我們解包的時(shí)候建議還是使用as?

as: 如果能確定百分百轉(zhuǎn)換成功的,可以不加符號(hào),直接使用as,比如:


var num1 = 10 as Float
var num2 = num1 as Any

注意: 任何類型都可以使用 as 轉(zhuǎn)換為 Any 類型,但是不能使用 as 轉(zhuǎn)換回去.必須使用 as? , as! 轉(zhuǎn)換回去.
比如:

X.self. , X.Type , AnyClass(X表示的是類)

如下所示代碼:


class Person{
    
}

var p = Person()

它的內(nèi)存分布如下:


全局變量p存儲(chǔ)的是Person實(shí)例對(duì)象的前8個(gè)字節(jié)的地址.這8個(gè)字節(jié)就是Person元類型地址.

X.self:是一個(gè)元類型(metadata)的地址,metadata中存放的是類型相關(guān)的信息.

也就是說X.self返回的是Person實(shí)例對(duì)象的前8個(gè)字節(jié)中存放的內(nèi)存地址,我們將通過匯編證明這一點(diǎn).

實(shí)例代碼:


class Person{
    
}

var p = Person()
var personType = Person.self

以上代碼的匯編如下:

Perso.self 匯編分析

從上圖可以看到,personType中存放的內(nèi)容和Person對(duì)象的前8個(gè)字節(jié)的內(nèi)容相同.

X.selfX.Type類型的:

image.png

所以,也可以這樣定義:

元類型的繼承
元類型同樣也支持繼承,父類metatype可以指向子類metatype:

AnyObject.Type: 上面講過AnyObject是指任意類類型,所以AnyObject.Type表示任意類類型的metadata.

在 Swift 有這樣的定義:


public typealias AnyClass = AnyObject.Type

AnyClass也表示任意類類型的metaclass

AnyClass = AnyObject.Type

type(of: <T>):傳入一個(gè)實(shí)例對(duì)象,返回這個(gè)實(shí)例對(duì)象的元類型.

type(of:)

type(of:)表面上看起來很像一個(gè)函數(shù),但是我們窺探它的匯編發(fā)現(xiàn)它的本質(zhì)就是直接取出實(shí)例對(duì)象的前8個(gè)字節(jié),并沒有函數(shù)調(diào)用:

type(of:) 匯編本質(zhì)

元類型的應(yīng)用

一: 通過類名創(chuàng)建控制器實(shí)例,類似于 OC 中的 xx.class


    override func viewDidLoad() {
        super.viewDidLoad()

        var vcs = [UIViewController.Type]()
        vcs.append(HomeViewController.self)
        vcs.append(MineViewController.self)
        
        for vc in vcs{
           //通過元類型創(chuàng)建視圖控制器
            let childVC = vc.init()
            self.addChild(childVC)
        }
        
    }

二: 通過父類元類型,創(chuàng)建子類實(shí)例.注意:父類中的 init() 方法必須加上 required ,因?yàn)楸仨氁_保子類都有 init() 方法的實(shí)現(xiàn).


class Person{
    required init(){
        
    }
}

class Student: Person {}
class Teacher: Person {}
class Driver: Person {}

func createPerson(pTypes: [Person.Type]) -> [Person]{
    
    var persons = [Person]()
    for pType in pTypes{
        
        persons.append(pType.init())
    }
    return persons
}

Self:一般用作方法返回值,用來限定方法返回值和方法調(diào)用者必須是同一類型,類似OC中的instancetype

如上圖所示,testPerson類中的實(shí)例方法,返回值是Self.即方法調(diào)用者和返回值必須是同一類型.為什么返回Person實(shí)例對(duì)象報(bào)錯(cuò)了呢?因?yàn)?Person 可能也會(huì)有子類,所以不能寫死

所以要這樣寫:

但是通過元類型創(chuàng)建實(shí)例對(duì)象必須要加上required:

Self也可表示當(dāng)前類型:

比如說下面代碼:


class Person{
    var age = 0
    static var name = "佚名"
    func makeOneSelf(){
        print("my name is \(Self.name) , age is \(self.age)")
    }
}

Self代表當(dāng)前類型,也就是Person類型,所以能調(diào)用類型屬性;
self表示調(diào)用方法的當(dāng)前實(shí)例對(duì)象.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末袭灯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌凭迹,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,430評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件折欠,死亡現(xiàn)場離奇詭異泻红,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)贡茅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來其做,“玉大人顶考,你說我怎么就攤上這事⊙梗” “怎么了驹沿?”我有些...
    開封第一講書人閱讀 167,834評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蹈胡。 經(jīng)常有香客問我渊季,道長,這世上最難降的妖魔是什么罚渐? 我笑而不...
    開封第一講書人閱讀 59,543評(píng)論 1 296
  • 正文 為了忘掉前任却汉,我火速辦了婚禮,結(jié)果婚禮上荷并,老公的妹妹穿的比我還像新娘合砂。我一直安慰自己,他們只是感情好源织,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評(píng)論 6 397
  • 文/花漫 我一把揭開白布翩伪。 她就那樣靜靜地躺著,像睡著了一般谈息。 火紅的嫁衣襯著肌膚如雪缘屹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,196評(píng)論 1 308
  • 那天侠仇,我揣著相機(jī)與錄音囊颅,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛踢代,可吹牛的內(nèi)容都是我干的盲憎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼胳挎,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼饼疙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起慕爬,我...
    開封第一講書人閱讀 39,671評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤窑眯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后医窿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體磅甩,經(jīng)...
    沈念sama閱讀 46,221評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評(píng)論 3 340
  • 正文 我和宋清朗相戀三年姥卢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了卷要。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,444評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡独榴,死狀恐怖僧叉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情棺榔,我是刑警寧澤瓶堕,帶...
    沈念sama閱讀 36,134評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站症歇,受9級(jí)特大地震影響郎笆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜忘晤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評(píng)論 3 333
  • 文/蒙蒙 一宛蚓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧德频,春花似錦苍息、人聲如沸缩幸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽表谊。三九已至钞护,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間爆办,已是汗流浹背难咕。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人余佃。 一個(gè)月前我還...
    沈念sama閱讀 48,837評(píng)論 3 376
  • 正文 我出身青樓暮刃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親爆土。 傳聞我的和親對(duì)象是個(gè)殘疾皇子椭懊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評(píng)論 2 359