初始化的過程包括爲每個存儲屬性設置一個初始值和其餘步驟。經過定義構造函數來實現初始化的過程,跟oc的初始化函數不一樣,Swift的構造函數不返回一個值。它們的主要角色是確保一個類型的實例在初次使用前被正確的初始化。swift
類的實例也能夠有析構函數,析構函數在類的實例在釋放前完成一些清理工做。安全
類和結構體必須爲它們全部的存儲屬性設置一個初始值,在類或結構體的實例建立完成前。存儲屬性不能是不肯定的狀態。ide
能夠經過構造函數或默認值的方式給存儲屬性一個初始值,並且經過這兩種方式,屬性的值都是被直接設置,不會調用屬性觀察者。函數
init() { }
你能夠自定義初始化過程,經過使用輸入參數,可選的屬性類型,或者在初始化期間給常量屬性賦值。具體描述在下面的部分中描述。3d
你能夠爲構造函數提供參數,看個例子:code
struct Celsius { var temperatureInCelsius: Double init(fromFahrenheit fahrenheit: Double) { temperatureInCelsius = (fahrenheit - 32.0) / 1.8 } init(fromKelvin kelvin: Double) { temperatureInCelsius = kelvin - 273.15 } init(_ celsius: Double) { temperatureInCelsius = celsius } } let boilingPointOfWater = Celsius(fromFahrenheit: 212.0) let freezingPointOfWater = Celsius(fromKelvin: 273.5) let bodyTemperature = Celsius(37.0)
Swift provides an autoamtic argument label for every parameter in an initializer if you don't provide one.看個例子:orm
struct Color { var red, green, blue: Double init(red: Double, green: Double, blue: Double) { self.red = red self.green = green self.blue = blue } init(white: Double) { red = white green = white blue = white } }
咱們能夠這樣初始化一個Color
的實例:blog
let megenta = Color(red: 1.0, green: 0.0, blue: 1.0) let halfGray = Color(white: 0.5)
參考上邊Celsius
類中的第三個構造方法,及bodyTemperature
的初始化。繼承
若是你自定義的類型有一個存儲屬性在邏輯上容許「沒有值」——或許是由於在初始化期間不能設置設置它的值,也多是在後邊的某個時間點容許它「沒有值」,那麼能夠把這個屬性聲明爲optional type。optional type的屬性被自動初始化爲nil
。原文以下:ip
「If your custom type has a stored property that is logically allowed to have 「no value」—perhaps because its value cannot be set during initialization, or because it is allowed to have 「no value」 at some later point—declare the property with an optional type. Properties of optional type are automatically initialized with a value of nil, indicating that the property is deliberately intended to have 「no value yet」 during initialization.」
Excerpt From: Apple Inc. 「The Swift Programming Language (Swift 3).」 iBooks.
看下下邊這個例子:
class SurveyQuestion { var text: String var response: String? init(text: String) { self.text = text } func ask() { print(text) } } let cheeseQuestion = SurveyQuestion(text: "Do you like cheese") cheeseQuestion.ask() cheeseQuestion.response = "YES, i do like chesse."
你能夠在初始化期間給一個常量屬性賦一個值,只要在初始化完成時有一個明確的值就能夠。
注意:對於類的實例,常量屬性只能由引入該屬性的類在初始化期間修改,不能被子類修改。
能夠在上邊那個類稍做修改,觀察text屬性:
class SurveyQuestion { let text: String var response: String? init(text: String) { self.text = text } func ask() { print(text) } } let cheeseQuestion = SurveyQuestion(text: "Do you like cheese") cheeseQuestion.ask() cheeseQuestion.response = "YES, i do like chesse."
在類或結構體爲每一個屬性提供默認值而且沒有其餘構造函數時,Swift提供默認構造函數。默認構造函數簡單的建立一個實例並把其全部屬性設置成默認值。
class ShoppingList { var name: String? var quantity = 1 var purchased = false } var item = ShoppingList()
上面的例子中,若是咱們不給quantity
設置默認值,編譯器將提示ShoppingList
類沒有構造函數。
結構體類型若是沒有自定義的構造函數,那麼會有一個memberwise initializer。
struct Size { var width = 0.0, height = 0.0 } let twoByTwo = Size(width: 2.0, height: 2.0)
Initializers can call other initializers to perform part of an instance's initialization. This process, known as initializer delegation, avoids duplicating code across multiple initializers.
對於值類型,可使用self.init
來引用該類型的其餘構造函數。self.init
只能在構造函數內使用。看個例子:
struct Size { var width = 0.0, height = 0.0 } struct Point { var x = 0.0, y = 0.0 } struct Rect { var size = Size() var point = Point() init() {} init(size: Size, point: Point) { self.size = size self.point = point } init(size: Size, center: Point) { let originX = center.x - size.width / 2 let originY = center.y - size.height / 2 self.init(size: size, point: Point(x: originX, y: originY)) } }
上面例子中第一個構造函數其實就至關於默認構造函數,第二個至關於memberwise initializer,第三個是徹底自定義的。這個例子還有一種寫法,能夠不寫init()
和init(size:point:)
,須要用到extension。
若是爲值類型自定義了一個構造函數,那就不能再訪問默認構造函數了。若是你想要你定義的值類型既可使用默認構造函數,也可使用自定義的構造函數,那須要把自定義的構造函數寫在extension中。
一個類全部的存儲屬性包括從父類繼承的屬性,都以徐在初始化幾千有一個初始值。
Swift爲類類型定義了兩種類型的構造函數來確保存儲屬性有一個初始值,分別是designated initializer和convenience initializer。在下面的討論中,咱們分別把這兩種稱爲指定構造函數、便利構造函數。
每一個類都必須至少有一個指定構造函數。在一些條件下,這個需求經過從父類繼承指定構造函數來知足。
便利構造函數能夠調用該類中的指定構造函數。
指定構造函數的語法:
便利構造函數的語法:
爲了簡化指定構造函數與遍歷構造函數之間的關係,Swift在構造函數間的delegation call上使用下邊三條規則:
能夠簡單的這樣記:
這裏有一個示意圖來講明上面描述的規則:
在Swift中類的初始化分爲兩個階段。在第一個階段,類的每個存儲屬性由引入其的類賦一個初始值,一旦每個存儲屬性的初始狀態肯定了,第二個幾段就開始了。第二個階段開始之後,每一個類在類可使用前都有機會給每個存儲屬性進行進一步自定義。
跟oc中的初始化有些許不一樣,主要體如今第一個階段,在第一個階段中,oc給每個屬性賦0值(如0、nil)。
Swift的編譯器執行四步安全性檢查(safaty-check)來確保兩個階段的初始化沒有問題:
self
as a value until after the first phase of initialization is complete.跟oc不一樣,Swift中的子類默認不繼承父類的構造方法。當你爲子類定義了一個構造方法,這個構造方法match一個父類的指定構造方法,你其實是對那個指定構造方法進行了重寫。所以,本身在子類構造方法前邊用override
標記。這個原則對於默認構造方法也適用。看個例子:
class Vehicle { var numberOfWheels = 0 var description: String { return "\(numberOfWheels) wheel(s)" } } class Bicycle : Vehicle { override init() { super.init() numberOfWheels = 2 } }
正如上邊提到的,默認狀況下,子類不繼承弗雷德構造方法。可是,在知足一些條件的前提下,父類的構造方法能夠被自動繼承。
假設你已經爲子類新引入的屬性提供了默認值,那麼有下面兩條規則適用:
這些規則即便在子類添加了便利構造方法時,依然適用。
在這一部分,咱們經過一個例子來解釋上邊的兩條規則。
class Food { var name: String //指定構造方法 init(name: String) { print("super versioin") self.name = name } //便利構造方法 convenience init() { self.init(name: "[Unnamed]") } } class RecipeIngredient: Food { var quantity: Int init(name: String, quantity: Int) { self.quantity = quantity super.init(name: name) } override convenience init(name: String) { print("sub version") self.init(name: name, quantity: 1) } }
如今這兩個類的構造方法能夠用下標的圖來清楚的展現:
咱們直接看一下RecipeIngredient
類,首先有一個自定義的指定構造方法init(name:quantity:)
,而後有一個自定義的便利構造方法init(name:)
,這個方法其實是重寫了Food
類的惟一的一個指定構造方法,因此知足了上面的第二條原則,所以自動繼承Food
類全部的便利構造方法,即init()
方法。這裏須要注意的是,RecipeIngredient
繼承來的init()
方法中,調用的init(name:)
不是Food
版本的,而是RecipeIngredient
版本的。
如今再來定義一個類:
class ShoppingListItem: RecipeIngredient { var purchased = false var description: String { var output = "\(quantity) * \(name)" output += purchased ? "✔️" : "❌" return output } }
由於ShoppingListItem
自己沒有定義任何的構造方法,所以它從父類繼承全部的構造方法。此時,示意圖以下: