Swift-初始化

【返回目錄】



初始化器

1.類、結(jié)構(gòu)體、枚舉都可以定義初始化器
2.類有兩種初始化器:指定初始化器(Designated Initializer)、便捷初始化器(Convenience Initializer)

//指定初始化器
init(paramenters) {
    statements
}
//便捷初始化器
convenience init(paramenters) {
    statements
}


3.每個類至少有一個指定初始化器,指定初始化器是類的主要初始化器
4.默認初始化器總是類的指定初始化器
5.類偏向于少量定制初始化器,一個類通常只有一個指定初始化器

  • 初始化器的相互調(diào)用規(guī)則
    • 指定初始化器必須從它的直系父類調(diào)用指定初始化器
    • 便捷初始化器必須從相同的類里調(diào)用另一個初始化器
    • 便捷初始化器最終必須調(diào)用一個指定初始化器
    • 確保使用任意的初始化器,都可以完整的初始化實例



兩段式初始化

在Swift編碼安全方面,為了保證初始化過程的安全,設定了兩段式初始化、安全檢查

  • 兩段式初始化

    • 第一階段:初始化所有存儲屬性
      • 外層調(diào)用 指定/便捷初始化器
      • 分配內(nèi)存給實例,但未初始化
      • 指定初始化器確保當前類定義的存儲屬性都初始化
      • 指定初始化器調(diào)用父類的初始化器,不斷向上調(diào)用,形成初始化器鏈
    • 第二階段:設置新的存儲屬性值
      • 從頂部初始化器向下,鏈中的每一個指定初始化器都有機會進一步制定實例
      • 初始化器現(xiàn)在能夠使用self(訪問、修改它的屬性,調(diào)用它的實例方法等)
      • 最終,鏈中任何便捷初始化器都有機會制定實例以及使用self
  • 安全檢查

    • 指定初始化器必須保證在調(diào)用父類初始化器之前,其所在類定義的所有存儲屬性都要初始化完成(完成賦值);
    • 指定初始化器必須先調(diào)用父類初始化器,然后才能為繼承的屬性設置新的值;
    • 便捷初始化器必須先調(diào)用同類中的其他初始化器,然后再為任意屬性設置新的值;
    • 初始化器在第一階段初始化完成之前,不能調(diào)用任何實例方法、不能讀取任何實例屬性的值、也不能引用self;
    • 直到第一階段結(jié)束,實例才算完全合法。




重寫初始化器

1.當重寫父類的指定初始化器時,必須加上override(即使子類的實現(xiàn)是便捷初始化器)

  • 父類的指定初始化器,被重寫為指定初始化器
    重寫的指定初始化器必須要調(diào)用父類的指定初始化器,還得先初始化當前類的存儲屬性;
override init(age: Int) {
    self.score = 0
    super.init(age: age)
}


  • 父類的指定初始化器,可以被重寫為便捷初始化器
override convenience init(age: Int) {
    super.init(age: age)
}


2.如果子類寫了一個匹配父類便捷出初始化器的初始化器,不用加上override

  • 因為父類的便捷初始化器永遠不會通過子類直接調(diào)用
  • 子類無法重寫父類的便捷初始化器。

自動繼承

1.如果子類沒有自定義任何指定初始化器,它會自動繼承父類所有的指定初始化器

class Person {
    var age: Int
    var name: String
    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
    init(age: Int) {
        self.age = age
        self.name = "Jack"
    }
}

class Student: Person {

}

var s1 = Student(age: 10)
var s2 = Student(age: 10, name: "Rose")


2.如果子類提供了父類所有指定初始化器的實現(xiàn)(要么通過方式1繼承,要么重寫);子類自動繼承所有的父類便捷初始化器。

3.就算子類添加了更多的便捷初始化器,這些規(guī)則仍然適用
4.子類以便捷初始化器的形式重寫父類的指定初始化器,也可以作為滿足規(guī)則2的一部分



必要構(gòu)造器(required)

1.用required修飾指定初始化器,表明其子類都必須實現(xiàn)該初始化器(通過繼承或者重寫實現(xiàn))

class SomeClass {
    required init() {
        // 構(gòu)造器的實現(xiàn)代碼
    }
}


2.如果子類重寫了required初始化器,也必須加required,不用加override

class SomeSubclass: SomeClass {
    required init() {
        // 構(gòu)造器的實現(xiàn)代碼
    ?       }
}




屬性觀察器

父類的屬性在它自己的初始化器中賦值不會觸發(fā)屬性觀察器,但在子類的初始化器中賦值會觸發(fā)屬性觀察器

class Person {
    var age: Int {
        willSet {
            print("Person willSet",newValue)
        }
        didSet {
            print("Person didSet",oldValue,age)
        }
    }
    init() {
        self.age = 0
    }
}

class Student: Person {
    override init() {
        super.init()
        self.age = 10
    }
}

var s1 = Student()
//Person willSet 10
//Person didSet 0 10




可失敗始化器

什么是可失敗初始化器?
- 調(diào)用這個初始化方法返回的對象可能是空(可選項)
- init?代表可失敗初始化器

1.類、結(jié)構(gòu)體、枚舉都可以使用init?定義可失敗初始化器

class Person {
    var name: String
    init?(name: String) {
        if name.isEmpty {
            return nil
        }
        self.name = name
    }
}
var p1 = Person(name: "") // nil
var p2 = Person(name: "Jack") //Jack


  • String轉(zhuǎn)換Int,也是可失敗初始化器
var num = Int("123")
public init?(_ description: String)


  • 枚舉的原始值
enum Answer: Int {
    case wrong, right
}
//var answer: Answer?
var answer = Answer(rawValue: 0)

枚舉一旦有原始值,是允許使用傳rawValue的初始化器;通過rawValue創(chuàng)建枚舉實例的初始化器是可失敗初始化器的。

2.不允許同時定義參數(shù)標簽、參數(shù)個數(shù)、參數(shù)類型相同的可失敗初始化器非可失敗初始化器

class Person {
    var name: String
    
    //可失敗初始化器
    init?(name: String) {
        if name .isEmpty {
            return nil
        }
        self.name = name
    }
    
    //非可失敗初始化器
    init(name: String) {
        self.name = name
    }
}

編譯器報錯:重復定義Invalid redeclaration of 'init(name:)'

3.可失敗初始化器可以調(diào)用非可失敗初始化器,非可失敗初始化器調(diào)用可失敗初始化器需要進行解包

  • init? 調(diào)用 init
class Person {
    var name: String

    //可失敗初始化器(便捷初始化器)
     convenience init?(name: String) {
        self.init()

        if name .isEmpty {
            return nil
        }
    }

    //非可失敗初始化器
    init() {
        self.name = ""
    }
}




  • init 調(diào)用 init?
class Person {
   var name: String

   //可失敗初始化器(便捷初始化器)
    init?(name: String) {
       if name .isEmpty {
           return nil
       }
        self.name = name
   }

   //非可失敗初始化器
    convenience init() {
       self.init(name: "")
   }
}

編譯器報錯:A non-failable initializer cannot delegate to failable initializer 'init(name:)' written with 'init?'

原因:非可失敗初始化器只能接受正常的Person對象,不能出現(xiàn)有空的情況,self.init(name: "")可失敗初始化器可能會出現(xiàn)空的。

因此,只需要在后面加個感嘆號!進行解包。

self.init(name: "")!




4.可以用init!定義隱式解包的可失敗初始化器

class Person {
   var name: String

   //可失敗初始化器(便捷初始化器)
    init!(name: String) {
       if name .isEmpty {
           return nil
       }
        self.name = name
   }

   //非可失敗初始化器
    convenience init() {
       self.init(name: "")
   }
}

??這樣寫會有問題,如果非可失敗初始化器self.init(name: "")傳過去的是空字符串,空字符串到可失敗初始化器就會返回nil,返回nil在非可失敗初始化器進行解包就會導致程序崩潰。



5.如果初始化器調(diào)用一個可失敗初始化器導致初始化失敗,那么整個初始化過程都失敗,并且之后的代碼都停止執(zhí)行

  • init? 調(diào)用 init?
    class Person {
        var name: String
        //可失敗初始化器
        init?(name: String) {
            if name .isEmpty {
                return nil
            }
            self.name = name
        }
    
        //可失敗初始化器
        convenience init?() {
            self.init(name: "")
            self.name = "Jack"
            print(".......")
        }
    }
    var person = Person()
    




6.可以用一個非可失敗初始化器重寫一個可失敗初始化器,但反過來是不行的。

  • 非可失敗初始化器可以重寫可失敗初始化器
class Student: Person {
    override init?(name: String) {
        super.init(name: name)
    }
}


  • 可失敗初始化器不可以重寫非可失敗初始化器
class Student: Person {
    override init?(name: String) {
        super.init(name: name)
    }
}

編譯器報錯:Failable initializer 'init(name:)' cannot override a non-failable initializer




反始化器(deinit)

  • deinit叫做反初始化器,類似于C++de析構(gòu)器、Objective-C的dealloc
  • 當類的實例對象被釋放內(nèi)存時,就會調(diào)用實例對象的deinit方法
class Person  {
    deinit {
        print("Person deinit")
    }
}

class Student: Person   {
    deinit {
        print("Person deinit")
    }
}






【上一篇】:繼承
【下一篇】:可選鏈




最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容