你真的弄懂了Swift初始化嗎?

什么是初始化

初始化就是:給存儲屬性賦值。像下面這樣

struct Fahrenheit {    
  var temperature: Double    
  init() {        
    temperature = 32.0    
  }
}

注意:當你對給一個屬性分配一個默認值的時候,它會調用它相對應的初始化方法,這個值是對屬性直接設置的,不會通知它對應的觀察者。

下面有2種初始化方法:

  • 默認初始化方法
    Swift為每一個結構或者基類提供了默認的構造器,來初始化它們所包含的所有屬性。默認構造器將會創建一個新的實例然后將它們的屬性設置為默認值
class ShoppingListItem {    
  var name: String?    
  var quantity = 1    
  var purchased = false
}
var item = ShoppingListItem()
  • 自定義初始化
    你可以根據輸入的參數來自定義初始化過程和可選的屬性類型,或者在初始化的時候修改靜態屬性。
    帶參數的初始化方法叫自定義初始化。
struct Celsius {    
  var temperatureInCelsius: Double = 0.0    

  init(fromFahrenheit fahrenheit: Double) {        
    temperatureInCelsius = (fahrenheit - 32.0) / 1.8    
  }    

  init(fromKelvin kelvin: Double) {        
      temperatureInCelsius = kelvin - 273.15    
  }
}

當然上面只是基礎知識幾個概念,下面才是重點。

類的繼承及其初始化

  1. 類的初始化過程

兩段式構造過程

Swift 中類的構造過程包含兩個階段。第一個階段,每個存儲型屬性通過引入它們的類的構造器來設置初始值。當每一個存儲型屬性值被確定后,第二階段開始,它給每個類一次機會在新實例準備使用之前進一步定制它們的存儲型屬性。

階段 1
某個指定構造器或便利構造器被調用;
完成新實例內存的分配,但此時內存還沒有被初始化;
指定構造器確保其所在類引入的所有存儲型屬性都已賦初值。存儲型屬性所屬的內存完成初始化;
指定構造器將調用父類的構造器,完成父類屬性的初始化;
這個調用父類構造器的過程沿著構造器鏈一直往上執行,直到到達構造器鏈的最頂部;
當到達了構造器鏈最頂部,且已確保所有實例包含的存儲型屬性都已經賦值,這個實例的內存被認為已經完全初始化。此時階段1完成。

Paste_Image.png

階段 2
從頂部構造器鏈一直往下,每個構造器鏈中類的指定構造器都有機會進一步定制實例。構造器此時可以訪問self、修改它的屬性并調用實例方法等等
最終,任意構造器鏈中的便利構造器可以有機會定制實例和使用self。

Paste_Image.png
  1. 如何初始化

1、自定義初始化方法要先調用自己類默認初始化方法,自己重寫默認初始化方法要先調用父類默認初始化方法
2、應該要先調用父類的構造器或者自身的默認構造器,以防止先給屬性賦值了然后才調用父類或者自身的默認構造器把以前的賦值覆蓋了

Paste_Image.png
  1. 關于初始化的繼承
    規則 1
    如果子類沒有定義任何指定構造器,它將自動繼承所有父類的指定構造器。

規則 2
如果子類提供了所有父類指定構造器的實現--不管是通過規則1繼承過來的,還是通過自定義實現的--它將自動繼承所有父類的便利構造器。

  1. Swift的4種安全檢查
    安全檢查 1
    指定構造器必須保證它所在類引入的所有屬性都必須先初始化完成,之后才能將其它構造任務向上代理給父類中的構造器。

安全檢查 2
指定構造器必須先向上代理調用父類構造器,然后再為繼承的屬性設置新值。如果沒這么做,指定構造器賦予的新值將被父類中的構造器所覆蓋。

安全檢查 3
便利構造器必須先代理調用同一類中的其它構造器,然后再為任意屬性賦新值。如果沒這么做,便利構造器賦予的新值將被同一類中其它指定構造器所覆蓋。

安全檢查 4
構造器在第一階段構造完成之前,不能調用任何實例方法、不能讀取任何實例屬性的值,也不能引用self的值。

代碼實例

class Food { 
    var name: String 
    //指定的初始化器
    init(name: String) { 
        self.name = name 
    } 
    //便攜初始化器
    convenience init() { 
        self.init(name: "[Unnamed]") 
    } 
} 
Paste_Image.png
class RecipeIngredient: Food { 
    var quantity: Int 
    //重載指定初始化器
    init(name: String, quantity: Int) { 
    //首先初始化自己本類的存儲屬性
        self.quantity = quantity 
    //然后調用初始化父類的存儲屬性
        super.init(name: name) 
    } 
    convenience init(name: String) { 
        self.init(name: name, quantity: 1) 
    } 
} 
Paste_Image.png
class ShoppingListItem: RecipeIngredient { 
    var purchased = false 
    var description: String { 
    var output = "\(quantity) x \(name.lowercaseString)" 
        output += purchased ? " ?" : " ?" 
        return output 
    } 
} 

由于它為自己引入的所有屬性都提供了默認值,并且自己沒有定義任何構造器,ShoppingListItem將自動繼承所有父類中的指定構造器和便利構造器。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 官方文檔 初始化 Initialization是為準備使用類,結構體或者枚舉實例的一個過程。這個過程涉及了在實例里...
    hrscy閱讀 1,149評論 0 1
  • 本章將會介紹 存儲屬性的初始賦值自定義構造過程默認構造器值類型的構造器代理類的繼承和構造過程可失敗構造器必要構造器...
    寒橋閱讀 781評論 0 0
  • 初始化(Initialization) 初始化是類、結構體、枚舉類型的準備過程。這個過程涉及到所有存儲屬性的初始化...
    泗哥閱讀 5,653評論 0 3
  • 構造過程是使用類、結構體或枚舉類型的實例之前的準備過程。在新實例可用前必須執行這個過程,具體操作包括設置實例中每個...
    莽原奔馬668閱讀 700評論 0 3
  • 123.繼承 一個類可以從另外一個類繼承方法,屬性和其他特征。當一個類繼承另外一個類時, 繼承類叫子類, 被繼承的...
    無灃閱讀 1,430評論 2 4