Swift: Initialization-1

初始化的過程包括爲每個存儲屬性設置一個初始值和其餘步驟。經過定義構造函數來實現初始化的過程,跟oc的初始化函數不一樣,Swift的構造函數不返回一個值。它們的主要角色是確保一個類型的實例在初次使用前被正確的初始化。swift

類的實例也能夠有析構函數,析構函數在類的實例在釋放前完成一些清理工做。安全

Setting Initial Values for Stored Properties

類和結構體必須爲它們全部的存儲屬性設置一個初始值,在類或結構體的實例建立完成前。存儲屬性不能是不肯定的狀態。ide

能夠經過構造函數或默認值的方式給存儲屬性一個初始值,並且經過這兩種方式,屬性的值都是被直接設置,不會調用屬性觀察者。函數

Initializers(構造函數)

init() {

}

Customizing Initialization(自定義初始化函數)

你能夠自定義初始化過程,經過使用輸入參數,可選的屬性類型,或者在初始化期間給常量屬性賦值。具體描述在下面的部分中描述。3d

1. Initialization Parameters

你能夠爲構造函數提供參數,看個例子: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)

Parameter Names and Argument Labels

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)

Initializer Parameters Without Argument Labels

參考上邊Celsius類中的第三個構造方法,及bodyTemperature的初始化。繼承

2. Optional Property Types

若是你自定義的類型有一個存儲屬性在邏輯上容許「沒有值」——或許是由於在初始化期間不能設置設置它的值,也多是在後邊的某個時間點容許它「沒有值」,那麼能夠把這個屬性聲明爲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."

3. Assigning Constant Properties During Initialization

你能夠在初始化期間給一個常量屬性賦一個值,只要在初始化完成時有一個明確的值就能夠。

注意:對於類的實例,常量屬性只能由引入該屬性的類在初始化期間修改,不能被子類修改。

能夠在上邊那個類稍做修改,觀察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."

Default Initializers(默認構造函數)

在類或結構體爲每一個屬性提供默認值而且沒有其餘構造函數時,Swift提供默認構造函數。默認構造函數簡單的建立一個實例並把其全部屬性設置成默認值。

class ShoppingList {
    var name: String?
    var quantity = 1
    var purchased = false
}

var item = ShoppingList()

上面的例子中,若是咱們不給quantity設置默認值,編譯器將提示ShoppingList類沒有構造函數。

Memberwise Initializers for Structure Types

結構體類型若是沒有自定義的構造函數,那麼會有一個memberwise initializer

struct Size {
    var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)

Initializer Delegation for Value Types

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中。

Class Inheritance and Initialization

一個類全部的存儲屬性包括從父類繼承的屬性,都以徐在初始化幾千有一個初始值。

Swift爲類類型定義了兩種類型的構造函數來確保存儲屬性有一個初始值,分別是designated initializerconvenience initializer。在下面的討論中,咱們分別把這兩種稱爲指定構造函數、便利構造函數。

每一個類都必須至少有一個指定構造函數。在一些條件下,這個需求經過從父類繼承指定構造函數來知足。

便利構造函數能夠調用該類中的指定構造函數。

Syntax for Designated and Convenience Initializers

指定構造函數的語法:

Designated Initalizer

便利構造函數的語法:

Convenience Initializer

Initializer Delegation for Class Types

爲了簡化指定構造函數與遍歷構造函數之間的關係,Swift在構造函數間的delegation call上使用下邊三條規則:

  1. A designated initializer must call a designated initializer from its immediate superclass.
  2. A convenience initializer must call another initializer from the same class.
  3. A convenience initiazlier must ultimately call a designated initializer.

能夠簡單的這樣記:

  • Designated initializers must always delegate up.
  • Convenience initiazlizers must always delegate across.

這裏有一個示意圖來講明上面描述的規則:

initializer_delegation_rules

Two-Phase Initialization

在Swift中類的初始化分爲兩個階段。在第一個階段,類的每個存儲屬性由引入其的類賦一個初始值,一旦每個存儲屬性的初始狀態肯定了,第二個幾段就開始了。第二個階段開始之後,每一個類在類可使用前都有機會給每個存儲屬性進行進一步自定義。

跟oc中的初始化有些許不一樣,主要體如今第一個階段,在第一個階段中,oc給每個屬性賦0值(如0、nil)。

Swift的編譯器執行四步安全性檢查(safaty-check)來確保兩個階段的初始化沒有問題:

  1. A designated initializer must ensure that all of the properties introduced by its class are initizlied before it delegates up to a superclass initializer.
  2. A designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property. If it doesn't, the new value the designated initializer assigns will be overwritten by the superclass as part of its own initialization.
  3. A convenience initializer must delegate to another initializer before assigning a value to any property. If it doesn't, the new value the convenience initializer assigns will be overwritten by its own class's designated initializer.
  4. An initializer cannot call any instance methods, read the value of any instance properties, or refer to self as a value until after the first phase of initialization is complete.

Initializer Inheritance and Overriding

跟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
    }
}

Automatic Initializer Inheritance

正如上邊提到的,默認狀況下,子類不繼承弗雷德構造方法。可是,在知足一些條件的前提下,父類的構造方法能夠被自動繼承。

假設你已經爲子類新引入的屬性提供了默認值,那麼有下面兩條規則適用:

  1. 若是子類沒有定義任何指定構造方法,那麼子類會自動繼承父類全部的指定構造方法。
  2. 若是子類提供了父類全部指定構造方法(不管是規則1提到的,仍是經過自定義實現),那麼子類自動繼承父類全部的便利構造方法。須要注意,子類能夠把父類指定構造方法實現爲便利構造方法,這樣也能夠知足這個條件

這些規則即便在子類添加了便利構造方法時,依然適用。

Designated and Convenience Initializer in Action

在這一部分,咱們經過一個例子來解釋上邊的兩條規則。

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

咱們直接看一下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自己沒有定義任何的構造方法,所以它從父類繼承全部的構造方法。此時,示意圖以下:

ShoppingListItem

相關文章
相關標籤/搜索