Swift-構(gòu)造器使用介紹

構(gòu)造過(guò)程:是使用類、結(jié)構(gòu)體或枚舉類型的實(shí)例之前的準(zhǔn)備過(guò)程。在新實(shí)例可用前必須執(zhí)行這個(gè)過(guò)程,具體操作包括設(shè)置實(shí)例中每個(gè)存儲(chǔ)型屬性的初始值和執(zhí)行其他必須的設(shè)置或初始化工作。

通過(guò)定義構(gòu)造器來(lái)實(shí)現(xiàn)構(gòu)造過(guò)程,這些構(gòu)造器可以看做是用來(lái)創(chuàng)建特定類型新實(shí)例的特殊方法。與 Objective-C 中的構(gòu)造器不同,Swift 的構(gòu)造器無(wú)需返回值,它們的主要任務(wù)是保證新實(shí)例在第一次使用前完成正確的初始化。

構(gòu)造器分為:指定構(gòu)造器、遍歷構(gòu)造器(convenience)


存儲(chǔ)屬性的初始賦值

類和結(jié)構(gòu)體在創(chuàng)建實(shí)例時(shí),必須為所有存儲(chǔ)型屬性設(shè)置合適的初始值。存儲(chǔ)型屬性的值不能處于一個(gè)未知的狀態(tài)。

方式1:可以在構(gòu)造器中為存儲(chǔ)型屬性賦初值

方式2:可以在定義屬性時(shí)為其設(shè)置默認(rèn)值

當(dāng)你為存儲(chǔ)型屬性設(shè)置默認(rèn)值或者在構(gòu)造器中為其賦值時(shí),它們的值是被直接設(shè)置的,不會(huì)觸發(fā)任何屬性觀察者。

最簡(jiǎn)單的,不帶參數(shù)的構(gòu)造器

struct Fahrenheit {

? var temperature: Double

? init() {

? ? temperature = 32.0

? }}

調(diào)用構(gòu)造器分為兩種方式:

var f = Fahrenheit()

var ff = Fahrenheit.init()

方式1:可以用類名加括號(hào)方式調(diào)用Fahrenheit(),如果有參數(shù),括號(hào)中間是參數(shù)名稱。

方式2:可以用類名加init方法調(diào)用Fahrenheit.init(),如果有參數(shù),init后面括號(hào)中間是參數(shù)名稱。


自定義構(gòu)造過(guò)程

struct Celsius {

? var temperatureInCelsius: Double

? init(fromFahrenheit fahrenheit: Double) {

? ? temperatureInCelsius = (fahrenheit - 32.0) / 1.8

? }

? init(fromKelvin kelvin: Double) {

? ? temperatureInCelsius = kelvin - 273.15

? }

}

let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)

let freezingPointOfWater = Celsius(fromKelvin: 273.15)

參數(shù)的內(nèi)部名稱和外部名稱:

構(gòu)造器并不像函數(shù)和方法那樣在括號(hào)前有一個(gè)可辨別的名字。因此在調(diào)用構(gòu)造器時(shí),主要通過(guò)構(gòu)造器中的參數(shù)名和類型來(lái)確定應(yīng)該被調(diào)用的構(gòu)造器。正因?yàn)閰?shù)如此重要,如果你在定義構(gòu)造器時(shí)沒(méi)有提供參數(shù)的外部名字,Swift 會(huì)為構(gòu)造器的每個(gè)參數(shù)自動(dòng)生成一個(gè)跟內(nèi)部名字相同的外部名。

struct Color {

? let red, green, blue: Double

? init(red: Double, green: Double, blue: Double) {

? ? self.red= red

? ? self.green= green

? ? self.blue= blue

}

let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)

let veryGreen = Color(0.0, 1.0, 0.0) // 報(bào)編譯時(shí)錯(cuò)誤,需要外部名稱

不帶外部名的構(gòu)造器參數(shù)

如果你不希望為構(gòu)造器的某個(gè)參數(shù)提供外部名字,你可以使用下劃線( _ )來(lái)顯式描述它的外部名,以此重寫上面所說(shuō)的默認(rèn)行為

structCelsius2 {

? var temperatureInCelsius: Double

? init(_ celsius: Double){

? ? temperatureInCelsius= celsius

? }

}

let bodyTemperature = Celsius2(37.0)


構(gòu)造過(guò)程中常量屬性的修改

你可以在構(gòu)造過(guò)程中的任意時(shí)間點(diǎn)給常量屬性指定一個(gè)值,只要在構(gòu)造過(guò)程結(jié)束時(shí)是一個(gè)確定的值。一旦常量屬性被賦值,它將永遠(yuǎn)不可更改。

class SurveyQuestion2 {

? let text: String

? init(text: String) {

? ? self.text= text

? }

}

let beetsQuestion = SurveyQuestion2(text: "How about beets?”)

默認(rèn)構(gòu)造器

如果結(jié)構(gòu)體或類的所有屬性都有默認(rèn)值,同時(shí)沒(méi)有自定義的構(gòu)造器,那么 Swift 會(huì)給這些結(jié)構(gòu)體或類提供一個(gè)默認(rèn)構(gòu)造器(default initializers)。這個(gè)默認(rèn)構(gòu)造器將簡(jiǎn)單地創(chuàng)建一個(gè)所有屬性值都設(shè)置為默認(rèn)值的實(shí)例

class ShoppingListItem {

? var name: String?

? var quantity = 1

? var purchased = false

}

var item = ShoppingListItem()

由于 ShoppingListItem 類中的所有屬性都有默認(rèn)值,且它是沒(méi)有父類的基類,它將自動(dòng)獲得一個(gè)可以為所有屬性設(shè)置默認(rèn)值的默認(rèn)構(gòu)造器(盡管代碼中沒(méi)有顯式為 name 屬性設(shè)置默認(rèn)值,但由于 name 是可選字符串類型,它將默認(rèn)設(shè)置為 nil)。

結(jié)構(gòu)體的逐一成員構(gòu)造器

1、除了上面提到的默認(rèn)構(gòu)造器,如果結(jié)構(gòu)體沒(méi)有提供自定義的構(gòu)造器,它們將自動(dòng)獲得一個(gè)逐一成員構(gòu)造器,即使結(jié)構(gòu)體的存儲(chǔ)型屬性沒(méi)有默認(rèn)值。

2、逐一成員構(gòu)造器是用來(lái)初始化結(jié)構(gòu)體新實(shí)例里成員屬性的快捷方法。我們?cè)谡{(diào)用逐一成員構(gòu)造器時(shí),通過(guò)與成員屬性名相同的參數(shù)名進(jìn)行傳值來(lái)完成對(duì)成員屬性的初始賦值。

struct Size1 {

? var width = 0.0, height = 0.0

}

let twoByTwo = Size1(width: 2.0, height: 2.0)

構(gòu)造器代理

構(gòu)造器可以通過(guò)調(diào)用其它構(gòu)造器來(lái)完成實(shí)例的部分構(gòu)造過(guò)程。這一過(guò)程稱為構(gòu)造器代理,它能減少多個(gè)構(gòu)造器間的代碼重復(fù)。

struct Size {

? var width = 0.0, height = 0.0

}

struct Point {

? var x = 0.0, y = 0.0

}

struct Rect {

? var origin = Point()

? var size = Size()

? init() {}

? init(origin: Point, size: Size) {

? ? self.origin= origin

? ? self.size= size

? }

? init(center: Point, size: Size) {

? ? let originX = center.x - (size.width / 2)

? ? let originY = center.y - (size.height / 2)

? ? self.init(origin: Point(x: originX, y: originY), size: size)// 構(gòu)造器代理

? }

}


類的繼承和構(gòu)造過(guò)程

1、類里面的所有存儲(chǔ)型屬性(包括所有繼承自父類的屬性)都必須在構(gòu)造過(guò)程中設(shè)置初始值。

2、Swift為類類型提供了兩種構(gòu)造器來(lái)確保實(shí)例中所有存儲(chǔ)型屬性都能獲得初始值,它們分別是 指定構(gòu)造器 和 便利構(gòu)造器。

指定構(gòu)造器

指定構(gòu)造器是類中最主要的構(gòu)造器。一個(gè)指定構(gòu)造器將初始化類中提供的所有屬性,并根據(jù)父類鏈往上調(diào)用父類的構(gòu)造器來(lái)實(shí)現(xiàn)父類的初始化。

每一個(gè)類都必須擁有至少一個(gè)指定構(gòu)造器。在某些情況下,許多類通過(guò)繼承了父類中的指定構(gòu)造器而滿足了這個(gè)條件。

便利構(gòu)造器

便利構(gòu)造器是類中比較次要的、輔助型的構(gòu)造器。你可以定義便利構(gòu)造器來(lái)調(diào)用同一個(gè)類中的指定構(gòu)造器,并為其參數(shù)提供默認(rèn)值。你也可以定義便利構(gòu)造器來(lái)創(chuàng)建一個(gè)特殊用途或特定輸入值的實(shí)例。

你應(yīng)當(dāng)只在必要的時(shí)候?yàn)轭愄峁┍憷麡?gòu)造器,比方說(shuō)某種情況下通過(guò)使用便利構(gòu)造器來(lái)快捷調(diào)用某個(gè)指定構(gòu)造器,能夠節(jié)省更多開發(fā)時(shí)間并讓類的構(gòu)造過(guò)程更清晰明了。

指定構(gòu)造器 和 便利構(gòu)造器 的語(yǔ)法

類的指定構(gòu)造器的寫法跟值類型簡(jiǎn)單構(gòu)造器一樣

init(parameters) {

? statements

}

便利構(gòu)造器也采用相同樣式的寫法,但需要在 init 關(guān)鍵字之前放置 convenience 關(guān)鍵字,并使用空格將它們倆分開

convenience init(parameters) {

? statements

}

類的構(gòu)造器代理規(guī)則

規(guī)則 1 :指定構(gòu)造器必須調(diào)用其直接父類的的指定構(gòu)造器。

規(guī)則 2 :便利構(gòu)造器必須調(diào)用同類中定義的其它構(gòu)造器。

規(guī)則 3 :便利構(gòu)造器必須最終導(dǎo)致一個(gè)指定構(gòu)造器被調(diào)用。

指定構(gòu)造器必須總是向上代理

便利構(gòu)造器必須總是橫向代理

Swift中類的構(gòu)造過(guò)程包含兩個(gè)階段:

第一個(gè)階段,每個(gè)存儲(chǔ)型屬性被引入它們的類指定一個(gè)初始值。當(dāng)每個(gè)存儲(chǔ)型屬性的初始值被確定后,

第二階段開始,它給每個(gè)類一次機(jī)會(huì),在新實(shí)例準(zhǔn)備使用之前進(jìn)一步定制它們的存 儲(chǔ)型屬性。

兩段式構(gòu)造過(guò)程的使用讓構(gòu)造過(guò)程更安全,同時(shí)在整個(gè)類層級(jí)結(jié)構(gòu)中給予了每個(gè)類完全的靈活性。兩段式構(gòu)造過(guò)程可以防止屬性值在初始化之前被訪問(wèn),也可以防止屬性被另外一個(gè)構(gòu)造器意外地賦予不同的值。

構(gòu)造器的繼承和重寫

class Vehicle {

? var numberOfWheels = 0

? var description: String {

? ? return "\(numberOfWheels) wheel(s)"

? }

}

class Bicycle: Vehicle {

? override init() {

? ? super.init()

? ? numberOfWheels = 2

? }

}

let bicycle = Bicycle()print("Bicycle: \(bicycle.description)")

構(gòu)造器的自動(dòng)繼承

規(guī)則1 如果子類沒(méi)有定義任何指定構(gòu)造器,它將自動(dòng)繼承所有父類的指定構(gòu)造器。

規(guī)則2 如果子類提供了所有父類指定構(gòu)造器的實(shí)現(xiàn),無(wú)論是通過(guò)規(guī)則 1 繼承過(guò)來(lái)的,還是提供了自定義實(shí)現(xiàn),它將自動(dòng)繼承所有父類的便利構(gòu)造器


可失敗構(gòu)造器

如果一個(gè)類、結(jié)構(gòu)體或枚舉類型的對(duì)象,在構(gòu)造過(guò)程中有可能失敗,則為其定義一個(gè)可失敗構(gòu)造器。這里所指的“失敗”是指,如給構(gòu)造器傳入無(wú)效的參數(shù)值,或缺少某種所需的外部資源,又或是不滿足某種必要的條件等。

為了妥善處理這種構(gòu)造過(guò)程中可能會(huì)失敗的情況。你可以在一個(gè)類,結(jié)構(gòu)體或是枚舉類型的定義中,添加一個(gè)或多個(gè)可失敗構(gòu)造器。其語(yǔ)法為在 init 關(guān)鍵字后面添加問(wèn)號(hào)( init? )。

這個(gè)可失敗構(gòu)造器檢查傳入的參數(shù)是否為一個(gè)空字符串。如果為空字符串,則構(gòu)造失敗。否則, species 屬性被賦值,構(gòu)造成功。

struct Animal {

? let species: String

? init?(species: String) {

? ? if species.isEmpty {

? ? ? return nil

? ? }

? ? self.species= species

? }

}

重寫一個(gè)可失敗構(gòu)造器

class Document {

? var name: String?

? // 該構(gòu)造器創(chuàng)建了一個(gè) name 屬性的值為 nil 的 document 實(shí)例

? init () {}

? // 該構(gòu)造器創(chuàng)建了一個(gè) name 屬性的值為非空字符串的 document 實(shí)例

? init?(name: String) {

? ? self.name = name

? ? if name.isEmpty {

? ? return nil

? ? }

? }

}

class AutomaticallyNamedDocument: Document {

? override init () {

? ? super.init ()

? ? self.name = "[Untitled]"

? }

? //用一個(gè)非可失敗構(gòu)造器 init(name:) 重寫了父類的可失敗構(gòu)造器 init?(name:) 。

? override init(name: String) {

? ? super.init()

? ? if name.isEmpty {

? ? ? self.name = "[Untitled]"

? ? } else {

? ? ? self.name = name

? ? }

? }

}


必要構(gòu)造器

在類的構(gòu)造器前添加 required 修飾符表明所有該類的子類都必須實(shí)現(xiàn)該構(gòu)造器:

class SomeClass {

? required init () {

? ? // 構(gòu)造器的實(shí)現(xiàn)代碼

? }

}

在子類重寫父類的必要構(gòu)造器時(shí),必須在子類的構(gòu)造器前也添加 required 修飾符,表明該構(gòu)造器要求也應(yīng)用于繼承鏈后面的子類。在重寫父類中必要的指定構(gòu)造器時(shí),不需要添加 override 修飾符:

class SomeSubclass: SomeClass {

? required init () {

? ? // 構(gòu)造器的實(shí)現(xiàn)代碼

? }

}


析構(gòu)器

析構(gòu)器只適用于類類型,當(dāng)一個(gè)類的實(shí)例被釋放之前,析構(gòu)器會(huì)被立即調(diào)用。析構(gòu)器用關(guān)鍵字 deinit 來(lái)標(biāo)示,類似于構(gòu)造器要用 init 來(lái)標(biāo)示。

析構(gòu)過(guò)程原理

1、Swift 會(huì)自動(dòng)釋放不再需要的實(shí)例以釋放資源。Swift 通過(guò)自動(dòng)引用計(jì)數(shù)(ARC) 處理實(shí)例的內(nèi)存管理。通常當(dāng)你的實(shí)例被釋放時(shí)不需要手動(dòng)地去清理。但是,當(dāng)使用自己的資源時(shí),你可能需要進(jìn)行一些額外的清理。例如,如果創(chuàng)建了一個(gè)自定義的類來(lái)打開一個(gè)文件,并寫入一些數(shù)據(jù),你可能需要在類實(shí)例被釋放之前手動(dòng)去關(guān)閉該文件。

2、在類的定義中,每個(gè)類最多只能有一個(gè)析構(gòu)器,而且析構(gòu)器不帶任何參數(shù)

deinit {

? // 執(zhí)行析構(gòu)過(guò)程

}

3、析構(gòu)器是在實(shí)例釋放發(fā)生前被自動(dòng)調(diào)用。你不能主動(dòng)調(diào)用析構(gòu)器。子類繼承了父類的析構(gòu)器,并且在子類析構(gòu)器實(shí)現(xiàn)的最后,父類的析構(gòu)器會(huì)被自動(dòng)調(diào)用。即使子類沒(méi)有提供自己的析構(gòu)器,父類的析構(gòu)器也同樣會(huì)被調(diào)用。

4、因?yàn)橹钡綄?shí)例的析構(gòu)器被調(diào)用后,實(shí)例才會(huì)被釋放,所以析構(gòu)器可以訪問(wèn)實(shí)例的所有屬性,并且可以根據(jù)那些屬性可以修改它的行為(比如查找一個(gè)需要被關(guān)閉的文件)。

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

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

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