Swift學習—— initialization構造過程

      在Swift中對象的初始化方法分爲兩種,指定構造器(Designated Initializers)和便利構造器(Convenience Initializers)。記錄一下我對它的初步理解。swift

Swift構造過程

      其實就是OC的init初始化方法。OC中通常初始化某個類(以Person爲例,屬性有name和age),使用以init開頭的方法,以下:安全

在.h中暴露出聲明方法。
//默認name="",age=0
- (instancetype)init; 
//以name來初始化,默認age=0
- (instancetype)initWithName:(NSString *)name;
//賦值name和age來初始化
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
複製代碼

實現方法以下:bash

在.m中寫方法實現。

// 便利構造器
- (instancetype)init {
    return [self initWithName:@""];
}

// 便利構造器
- (instancetype)initWithName:(NSString *)name {
    return [self initWithName:name age:0];
}

// 指定構造器
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
    self = [super init];
    if (!self) {
        return nil;
    }
    _name = name;
    _age = age;
    return self;
}
複製代碼

而Swif中的三種也是這樣,init()以下。這裏我在ide

.swift中寫方法
//此處,我給name和age初始值。
var name: String = ""
var age: Int = 0

// 便利構造器
override convenience init() {
    self.init(name: "haha")//此處默認名字是haha,是爲了方便看出子類可繼承convenience init方法。
}

// 便利構造器
convenience init(name: String) {
    self.init(name: name, age:0)
}
   
// 指定構造器 
init(name: String, age: Int) {
    super.init()
    self.name = name
    self.age = age
}
複製代碼

寫Swift這段代碼時,有以下問題函數

  1. 若是沒對存儲屬性name和age進行初始化,會報錯,且須要在調用父類的構造方法super.init()方法以前須要對他倆賦值。
  2. 若是不帶convenience,Xcode會提示報錯,大意就是Designated initializer不能調self.init。而改爲convenience initializer就對了。而後咱們點fix,init 前面就會自動插入 convenience。至於爲何須要調用convenience,後面會作詳細說明。

注意:Swift不一樣於OC的init,它沒有返回值。在這一過程裏面初始全部的存儲屬性和其它的必要初始化操做。 ui

對存儲屬性初始化

      存儲屬性,在調用任何方法(好比子類調父類的super.init,或者調本類的方法)以前,都應該確保在本類對存儲屬性進行初始化。若是name和age並無給初始值,則代碼應該改爲以下,以init(name: String, age: Int)爲例:spa

init(name: String, age: Int) {
    self.name = name
    self.age = age

    super.init()
}
複製代碼

這是因爲Swift的內存安全,在調用父類構造方法以前,確保對本類的全部存儲屬性進行初始化。採用了Two-Phase Initialization(兩段式構造)模型來實現內存安全。code

  1. 初始化全部的存儲屬性。
  2. 它給每一個類一次機會,在新實例準備使用以前進一步自定義它們的存儲型屬性。

Github源碼文檔有一處是三段式構造,第三段:若是還有其它操做,可執行其它方法來完成初始化。對象

Designated Initializers

指定構造器,這是一個類最主要的方法,若是有父類,則子類的指定構造器方法必需要調父類的指定構造方法與父類關聯。好比上面的Swift的Person類,繼承

  1. 指定的構造器方法爲最後一個,name和age都傳入參數來初始化實例的那個。因此我理解的是最基礎的init函數,別的init函數須要來調用的那個函數。
  2. 指定的構造器方法只能調用super.init(若是有父類的話),不能夠調用self.init(平級的init函數)
  3. 固然,指定的構器並非必須只有一個,好比上面的根據name來生成實例的函數,若是也要寫成指定構造器,代碼以下:
init(name: String) {
    super.init()
    self.name = name
}
複製代碼
  1. 不能寫在extension擴展中。 若是初始化的時候,還須要調用別的方法,則這裏也須要加上。即把方法 init(name: String, age: Int)裏面的除了給age賦值代碼外,其它代碼都複製過來。

Convenience Initializers

便利構造器,便捷生成實例的方法,對指定構造器的參數作了選擇,可不用傳那麼多參數。

  1. 不能調用父類(若是有父類)的指定構造方法,只能調用self.init(平級的init函數)。且最後確定要調到指定構造方法。這也是開頭代碼必須使用便利方法的主要緣由。
  2. 不像指定構造器,它能夠寫在extension擴展中。
  3. 在調用self.init以前不能訪問self,或者依賴self的東西。
  4. 可被子類繼承。可是子類必須重寫父類的全部的指定構造方法。以上面Person類爲例,我又建立了一個它的子類Boy繼承Person。若是想用Boy.init()方法,則Boy類裏應該重寫Person類的全部的指定構造方法。代碼以下:
class Boy: Person {
    
    var girlFriendName: String
    
    override init(name: String, age: Int) {
        self.girlFriendName = "girl"
        super.init(name: name, age: age)
        print("我完成了init")
    }
}
複製代碼

若是Person裏面還有另外一個指定構造方法,則Boy類裏面也須要重寫此方法。不然調用Boy.init()會報錯。調用Boy.init()的構造過程以下:

  1. Boy對象的內存分配,可是此時並無初始化。
  2. Boy.init()會調用從父類繼承的convenience init方法。
  3. 而後根據代碼調用到Person的convenience init(name: String) 方法。
  4. 調回到Boy重寫的父類的init(name: String, age: Int)方法。
  5. 完成Boy類的全部存儲屬性的初始化。
  6. 調用父類Person的指定構造方法(而後完成Person類的全部存儲屬性的初始化,調用Person的父類的指定構造方法。會沿着類的繼承鏈依次往上找,完成全部的存儲屬性的初始化,也就是上面說的初始化的第一階段)
  7. 調用Person的從新賦值(若是有其它初始始化操做,也直接調用。而後調用Boy的print方法。也就是說第一階段完成後,就進行了第二階段,此階段能夠給存儲屬性從新賦值,也可調用其它方法來完成初始化,會依次從類的繼承鏈頂端向下執行),而後return。
  8. Person的遍歷方法返回。
相關文章
相關標籤/搜索