構(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)閉的文件)。