Swift中的多態(tài)脐帝、初始化同云、可選鏈

重寫override

  • 重寫類型方法、下標(biāo)

    1. class修飾的類型方法堵腹、下標(biāo)炸站,允許被子類重寫
    2. static修飾的類型方法、下標(biāo)疚顷,不允許被子類重寫

    注意:如果此時(shí)繼承父類重寫父類方法之后旱易,如果重寫的方法不想繼續(xù)被子類去重寫禁偎,那么可以此時(shí)在重寫的方法前面添加static關(guān)鍵字修飾來(lái)確保子類不能夠重寫當(dāng)前的方法

    class AnimalSubscript {
        func speak() {
            print("animal speak")
        }
        subscript(index: Int) -> Int {index}
        
        class func eat() {
            print("animal eat")
        }
        
        static func drink() {
            print("animal drink")
        }
    }
    
    class Cat: AnimalSubscript {
        override func speak() {
            print("cat speak")
        }
        
        override subscript(index: Int) -> Int {index + 20}
        override class func eat() {
            print("cat eat")
        }
        
        //此時(shí)重寫的方法將不能被子類重寫修改
//        override static func eat(){
//            print("終結(jié)當(dāng)前的方法")
//        }
        
    }
  • 重寫屬性
    1. 子類可以將父類的計(jì)算屬性、存儲(chǔ)屬性重寫為計(jì)算屬性
    2. 子類不可以將父類重寫為存儲(chǔ)屬性
    3. 只能重寫var變量屬性阀坏。不能重寫let屬性
    4. 重寫時(shí)如暖,屬性名、類型要一致
    5. 子類重寫后的屬性權(quán)限不能小于父類屬性的權(quán)限
    6. 如果父類屬性是只讀的忌堂,那么子類重寫后的屬性可以是只讀的盒至,也可以是可讀寫
    7. 如果父類屬性是可讀可寫的,那么子類重寫后的屬性也必須是可讀寫的
    class OverrideCircle {
        //懶加載
        lazy var lazyVar: Int = {
            return 1
        }()
        
        private(set) var onlyRead: Int = 0
        var radius: Int = 0
        var diameter: Int{
            get {
                print("get diameter")
                return (radius * 2)
            }
            
            set {
                radius = newValue
                print("Circle set diameter")
            }
        }
        
    }
    
    class OverrideCircleSon: OverrideCircle {
        
        var newRadius = 0
        override var diameter: Int {
            get {
                print("overrideCircleSon get value")
                return radius * 2
            }
            
            set {
                print("overrideCircleSon set value")
                radius = newValue
            }
        }
        
        override var radius: Int{
            get {
                print("reWrite stored perproty of radius")
                return newRadius
            }
            
            set {
                print("reGet sotre perproty of radius")
                newRadius = newValue
            }
        }
    }
  • 屬性觀察器
    1. 可以在子類為父類屬性(除了只讀計(jì)算屬性士修、let屬性)增加屬性觀察器
    2. set 與willset 枷遂、get 與 willget 均不能共存的
    3. 不管父類是實(shí)例屬性,類型屬性或者存儲(chǔ)屬性棋嘲,計(jì)算屬性酒唉,子類都可以為父類添加觀察屬性
    class perprotyObserverClass: OverrideCircle{
        override var radius: Int{
            willSet {
                print("will set value", newValue)
            }
            didSet {
                //此時(shí)的radius不會(huì)引發(fā)死循環(huán),因?yàn)榇藭r(shí)并沒有重寫計(jì)算屬性沸移,此時(shí)訪問的仍舊是radius本身痪伦,
                //屬性監(jiān)聽器的實(shí)現(xiàn)是通過(guò)中間臨時(shí)變量來(lái)實(shí)現(xiàn)的,所以不存在死循環(huán)
                print("set value finish", oldValue, radius)
            }
        }
        //不能夠正常添加觀察期監(jiān)聽
//        override var onlyRead: Int{
//            willSet {
//
//            }
//        }
        override var lazyVar: Int{
            willSet {
                print("lazy load will set")
            }
        }
    }
  • final 使用
    1. final修飾的方法雹锣、下標(biāo)网沾、屬性,禁止被重寫
    2. final修飾的類笆制,禁止被繼承
final class finalClass {
        final var radius: Int {
            set {
                print("radius write")
            }
            get {
                print("circle get radius")
                return 20
            }
        }
        
    }

多態(tài)

  1. 多態(tài)是指:父類指針指向子類對(duì)象的行為現(xiàn)象
  2. 在OC中多態(tài)是依靠runtime來(lái)實(shí)現(xiàn)的绅这,而Swift中的實(shí)現(xiàn)方式類似于C++中的虛函數(shù)表來(lái)實(shí)現(xiàn)的

幾個(gè)問題:

  1. 父類是如何判斷對(duì)應(yīng)的子類對(duì)象的類型的(多態(tài))
  2. 如果將class類型換成struct類型,那么在內(nèi)存中調(diào)用的時(shí)候有什么區(qū)別在辆?
    因?yàn)閷?duì)于結(jié)構(gòu)體來(lái)說(shuō),存儲(chǔ)在椂忍Γ空間匆篓,所以存儲(chǔ)的地址在編譯期間就已經(jīng)確定。而對(duì)象的實(shí)例寇窑,需要堆空間去動(dòng)態(tài)分配內(nèi)存鸦概,此時(shí)的堆空間的地址是動(dòng)態(tài)分配的,由此調(diào)用對(duì)應(yīng)的方法由于對(duì)象實(shí)例分配動(dòng)態(tài)的原因甩骏,并不能直接在編譯期間直接找到其內(nèi)存地址窗市,所以需要在運(yùn)行的時(shí)候去找到動(dòng)態(tài)分配的地址空間,進(jìn)而去通過(guò)該地址間接找到要調(diào)用的方法地址饮笛,因此對(duì)象的方法的調(diào)用地址是變化的咨察。所以相比于類調(diào)用方法,結(jié)構(gòu)體調(diào)用的方法執(zhí)行效率要比放在類中執(zhí)行的效率高很多

初始化器

  • 特點(diǎn)

    1. 類福青、結(jié)構(gòu)體摄狱、枚舉都可以定義初始化器
    2. 類有兩種初始化器:指定初始化器(designed initializer)脓诡、便捷初始化器(convenience initializer)
    3. 每個(gè)類至少有一個(gè)初始化器,指定初始化器是類的主要初始化器
    4. 默認(rèn)初始化器總是類的指定初始化器
    5. 類偏向于少量的指定初始化器媒役,一個(gè)類通常只有一個(gè)指定初始化器
    6. 初始化過(guò)程(為了保證初始化安全祝谚,設(shè)定了兩段式初始化安全檢查)
  • 初始化器的相互調(diào)用規(guī)則

    1. 指定初始化器必須從它的直系父類調(diào)用指定初始化器
    2. 便攜初始化器必須從相同的類里調(diào)用另一個(gè)初始化器
    3. 便攜初始化器最終必須調(diào)用一個(gè)指定初始化器
    class Person {
        var name: String
        init(name: String) {
            self.name = name
        }
    }
    
    class Student: Person {
        
        var score: Int
        var height: Double
        
        //指定初始化器
        init(score: Int, height: Double) {
            self.score = score
            self.height = height
            super.init(name: "student")
        }
        
        //便攜初始化器
        convenience init(score: Int) {
            self.init(score:99, height: 20.0)
            self.score = score
        }
        
        //重寫父類的指定初始化器為便捷初始化器
        convenience override init(name: String) {
            self.init(score: 98, height: 177)
        }
        
    }
  • 兩段式初始化過(guò)程

    • 初始化所有的存儲(chǔ)屬性
      1. 外層調(diào)用指定/便攜初始化器
      2. 分配內(nèi)存給實(shí)例酣衷,但未初始化
      3. 指定初始化器來(lái)確保當(dāng)前類定義的存儲(chǔ)屬性都初始化
      4. 指定初始化器調(diào)用父類的初始化器交惯,不斷向上調(diào)用,形成初始化器鏈

    • 設(shè)置新的存儲(chǔ)屬性值
      1. 從頂部初始化器往下穿仪,鏈中的每一個(gè)指定初始化器都有機(jī)會(huì)進(jìn)一步定制實(shí)例
      2. 初始化器現(xiàn)在能夠使用self(訪問商玫、修改它的屬性,調(diào)用它的實(shí)例方法等等)
      3. 最終牡借,鏈中的任何便捷初始化器都有機(jī)會(huì)定制實(shí)例以及使用self

    • 如果存在繼承關(guān)系拳昌,初始化順序如下:

      1. 先初始化指定初始化器完成子類的初始化
      2. 依次調(diào)用父類的指定初始化器完成初始化操作
    • 安全檢查
      1. 指定初始化器必須保證在調(diào)用父類初始化之前,其所在類定義的所有存儲(chǔ)屬性都要初始化完成
      2. 指定初始化器必須先調(diào)用父類初始化器钠龙,然后才能為繼承的屬性設(shè)置新值
      3. 便捷初始化器必須先調(diào)用同類中的其他初始化器炬藤,然后再為任意屬性設(shè)置新值
      4. 初始化器在第一階段初始化完成之前,不能調(diào)用任何實(shí)例方法碴里,不能讀取任何實(shí)例屬性的值沈矿,也不能引用self
      5. 直到第一階段完成,實(shí)例才能算完全合法

    • 重寫父類的指定初始化器
      1. 必須加上override(即使子類實(shí)現(xiàn)的是便捷初始化器)
      2. 因?yàn)楦割惖谋憬莩跏蓟饔肋h(yuǎn)不會(huì)通過(guò)子類直接調(diào)用咬腋,所以羹膳,子類無(wú)法重寫父類的便捷初始化器

  • 自動(dòng)繼承

    • 特點(diǎn)
    1. 如果子類沒有定義任何初始化器,那么它將自動(dòng)繼承父類所有的指定初始化器
    2. 如果子類提供了父類所有指定初始化器的實(shí)現(xiàn)(一種是全部重寫根竿、另一種是通過(guò)步驟1來(lái)實(shí)現(xiàn))陵像,此時(shí)子類將會(huì)自動(dòng)繼承所有的父類的便捷初始化器
    3. 就算子類添加很多便捷初始化器,以上特點(diǎn)依舊適用
    4. 子類用便捷初始化器的形式重寫父類的指定初始化器寇壳,此時(shí)步驟2也適用于這種情況
  • required使用

    • 特點(diǎn)
    1. required修飾指定初始化器醒颖,表明其所有子類都必須實(shí)現(xiàn)該初始化器(通過(guò)繼承或者重寫來(lái)實(shí)現(xiàn))
    2. 如果子類重寫了required初始化器,也必須加上required壳炎,不用加override
  • 初始化引發(fā)的屬性觀察器的變化
    特點(diǎn):
    1. 父類的屬性在它自己的初始化器中賦值不會(huì)觸發(fā)屬性觀察器
    2. 在子類的初始化器中賦值會(huì)觸發(fā)屬性觀察器

class Animal {
        var type: String {
            didSet {
                print("oldValue:\(oldValue), new value:\(type)")
            }
            willSet {
                print("newValue:\(newValue)")
            }
        }
        
        required init(type: String) {
            self.type = type
        }
    }
    
    class Dog: Animal {
        
        var age: Int
        init(age: Int) {
            self.age = age
            super.init(type: "dog")
        }
        
        required init(type: String) {
            self.age = 0
            super.init(type: "dog")
            self.type = "pig"
        }
        
    }
  • 反初始化器 deinit

    1. 類似于C++的析構(gòu)函數(shù)泞歉、OC中的dealloc方法
    2. 當(dāng)類的實(shí)例對(duì)象被釋放內(nèi)存的時(shí)候,會(huì)調(diào)用實(shí)例對(duì)象的deinit方法
    3. deinit不能接受任何參數(shù)匿辩、不能寫小括號(hào)腰耙、不能自行調(diào)用
    4. 父類的deinit能被子類繼承
    5. 子類的deinit實(shí)現(xiàn)執(zhí)行完畢后,會(huì)調(diào)用父類的deinit
  • 可失敗初始化器
    特點(diǎn):

    1. 類铲球、結(jié)構(gòu)體挺庞、枚舉都可以使用init?定義可失敗初始化器
    2. 不允許同時(shí)定義參數(shù)標(biāo)簽、參數(shù)個(gè)數(shù)睬辐、參數(shù)類型相同的可失敗初始化器非可失敗初始化器
    3. 可失敗初始化器可以調(diào)用非可失敗初始化器挠阁,非可失敗初始化器調(diào)用可失敗初始化器需要進(jìn)行解包操作
    4. 如果初始化器調(diào)用一個(gè)可失敗初始化器導(dǎo)致初始化失敗宾肺,那么整個(gè)初始化過(guò)程都會(huì)失敗,并且之后的代碼都會(huì)停止
    5. 可以用一個(gè)非可失敗初始化器重寫一個(gè)可失敗初始化器侵俗,但反過(guò)來(lái)是不行的
    6. 可以通過(guò)可選鏈來(lái)間接的進(jìn)行判空容錯(cuò)(如果為nil锨用,不執(zhí)行后序語(yǔ)句內(nèi)容)
class PersonCanFailed {
        var name: String
        var age: Int?
        init?(name: String) {
            if name.isEmpty {
                return nil
            }
            self.name = name
        }
        
        //定義一個(gè)便捷初始化器
        convenience init?() {
            self.init(name: "leo") //如果初始化失敗,后序代碼將不會(huì)再執(zhí)行
            self.age = 10
        }
        
        //已經(jīng)接觸到的可失敗初始化器
        func demo() {
            //可能初始化失敗
            let _ = Int("12345555")
            enum Answer: Int{
                case wrong, right
            }
            //可能初始化失敗
            let _ = Answer(rawValue: 1)
        }
    }

    func testOptinolChainUse() {
        var scores = ["Jack": [89, 99, 67],
                      "Rose": [89, 99, 67]]
        
        scores["Jack"]?[0] = 100
        scores["Rose"]?[2] = 87
        
        //判斷下面num1與num2 內(nèi)容是什么類型
        var num1: Int? = 5
        num1? = 10   //Optinoal(10)
        
        //num2? 等同于判定num2是否為空隘谣,如果為空后序賦值操作將取消增拥,如果不為空則正常操作
        var num2: Int? = nil
        num2? = 10  //nil
        num2 = 19 //19
    }
    
    func testOptionalChainDictUse() {
        var dict:[String: (Int, Int) -> Int] = ["sum":(+), "difference":(-)]
        var result = dict["sum"]?(10, 20) //Optional(30), Int?
        
    }

可選鏈 ?

  • 可選鏈調(diào)用特點(diǎn)

    1. ?的作用是判斷調(diào)用者是否有值,有的話會(huì)繼續(xù)調(diào)用后序方法寻歧,沒有的話掌栅,直接返回,返回的內(nèi)容是可選類型(這個(gè)行為改變了原有的返回?cái)?shù)據(jù)類型码泛,將之前返回的內(nèi)容包裝成可選類型了)
    2. 方法沒有返回值卻能夠使用var進(jìn)行接收猾封,原因是方法的調(diào)用默認(rèn)返回值是Void,可以認(rèn)定是一個(gè)空元組類型
    3. 可以通過(guò)可選鏈接收返回值的方法來(lái)判定方法是否調(diào)用成功
  • 其他需要注意的點(diǎn)

    1. 如果可選為nil噪珊,調(diào)用方法晌缘、下標(biāo)、屬性失效痢站,結(jié)果為nil
    2. 如果可選項(xiàng)不為nil磷箕,調(diào)用方法、下標(biāo)阵难、屬性成功岳枷,結(jié)果會(huì)被包裝成可選類型
    3. 如果結(jié)果本來(lái)就是可選項(xiàng),不會(huì)進(jìn)行再包裝
    4. 多個(gè)?可選可以鏈接在一起
    5. 如果可選鏈中任何一個(gè)節(jié)點(diǎn)是nil呜叫,那么整個(gè)可選鏈就會(huì)調(diào)用失敗
func testOptionlUse() {
        var person: CarPerson? = CarPerson()
        var age = person?.age()  //可選鏈調(diào)用
        var age1 = person!.age() //強(qiáng)制解包
        var name = person?.name  //可選鏈調(diào)用
        var result = person?.eat() //沒有返回值仍能夠通過(guò)變量去接受返回值
        
        if let _ = person?.eat() {
            print("調(diào)用eat成功")
        }else{
            print("調(diào)用eat失敗")
        }
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末空繁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子怀偷,更是在濱河造成了極大的恐慌家厌,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件椎工,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蜀踏,警方通過(guò)查閱死者的電腦和手機(jī)维蒙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)果覆,“玉大人颅痊,你說(shuō)我怎么就攤上這事【执” “怎么了斑响?”我有些...
    開封第一講書人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵菱属,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我舰罚,道長(zhǎng)纽门,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任营罢,我火速辦了婚禮赏陵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘饲漾。我一直安慰自己蝙搔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開白布考传。 她就那樣靜靜地躺著吃型,像睡著了一般。 火紅的嫁衣襯著肌膚如雪僚楞。 梳的紋絲不亂的頭發(fā)上勤晚,一...
    開封第一講書人閱讀 52,328評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音镜硕,去河邊找鬼运翼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛兴枯,可吹牛的內(nèi)容都是我干的血淌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼财剖,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼悠夯!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起躺坟,我...
    開封第一講書人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤沦补,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后咪橙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體夕膀,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年美侦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了产舞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡菠剩,死狀恐怖易猫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情具壮,我是刑警寧澤准颓,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布哈蝇,位于F島的核電站,受9級(jí)特大地震影響攘已,放射性物質(zhì)發(fā)生泄漏炮赦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一贯被、第九天 我趴在偏房一處隱蔽的房頂上張望眼五。 院中可真熱鬧,春花似錦彤灶、人聲如沸看幼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)诵姜。三九已至,卻和暖如春搏熄,著一層夾襖步出監(jiān)牢的瞬間棚唆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工心例, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宵凌,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓止后,卻偏偏與公主長(zhǎng)得像瞎惫,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子译株,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

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