在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ iOS/ 構(gòu)造過程(Initialization)
方法 - Methods
關(guān)于 Swift
下標(biāo)腳本(Subscripts)
類和結(jié)構(gòu)體
類型轉(zhuǎn)換(Type Casting)
控制流
析構(gòu)過程(Deinitialization)
集合類型 (Collection Types)
構(gòu)造過程(Initialization)
Optional Chaining
枚舉(Enumerations)
自動(dòng)引用計(jì)數(shù)
繼承(Inheritance)
擴(kuò)展(Extensions)
泛型
字符串和字符(Strings and Characters)
函數(shù)(Functions)
高級(jí)運(yùn)算符
訪問控制
基本運(yùn)算符
嵌套類型
閉包(Closures)
協(xié)議
屬性 (Properties)

構(gòu)造過程(Initialization)

構(gòu)造過程是為了使用某個(gè)類、結(jié)構(gòu)體或枚舉類型的實(shí)例而進(jìn)行的準(zhǔn)備過程。這個(gè)過程包含了為實(shí)例中的每個(gè)屬性設(shè)置初始值和為其執(zhí)行必要的準(zhǔn)備和初始化任務(wù)。

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

類實(shí)例也可以通過定義析構(gòu)器(deinitializer)在類實(shí)例釋放之前執(zhí)行特定的清除工作。想了解更多關(guān)于析構(gòu)器的內(nèi)容,請(qǐng)參考析構(gòu)過程

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

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

你可以在構(gòu)造器中為存儲(chǔ)型屬性賦初值,也可以在定義屬性時(shí)為其設(shè)置默認(rèn)值。以下章節(jié)將詳細(xì)介紹這兩種方法。

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

構(gòu)造器

構(gòu)造器在創(chuàng)建某特定類型的新實(shí)例時(shí)調(diào)用。它的最簡(jiǎn)形式類似于一個(gè)不帶任何參數(shù)的實(shí)例方法,以關(guān)鍵字init命名。

下面例子中定義了一個(gè)用來保存華氏溫度的結(jié)構(gòu)體Fahrenheit,它擁有一個(gè)Double類型的存儲(chǔ)型屬性temperature

    struct Fahrenheit {
        var temperature: Double
        init() {
            temperature = 32.0
        }
    }
    var f = Fahrenheit()
    println("The default temperature is \(f.temperature)° Fahrenheit")
    // 輸出 "The default temperature is 32.0° Fahrenheit”

這個(gè)結(jié)構(gòu)體定義了一個(gè)不帶參數(shù)的構(gòu)造器init,并在里面將存儲(chǔ)型屬性temperature的值初始化為32.0(華攝氏度下水的冰點(diǎn))。

默認(rèn)屬性值

如前所述,你可以在構(gòu)造器中為存儲(chǔ)型屬性設(shè)置初始值;同樣,你也可以在屬性聲明時(shí)為其設(shè)置默認(rèn)值。

注意:
如果一個(gè)屬性總是使用同一個(gè)初始值,可以為其設(shè)置一個(gè)默認(rèn)值。無論定義默認(rèn)值還是在構(gòu)造器中賦值,最終它們實(shí)現(xiàn)的效果是一樣的,只不過默認(rèn)值將屬性的初始化和屬性的聲明結(jié)合的更緊密。使用默認(rèn)值能讓你的構(gòu)造器更簡(jiǎn)潔、更清晰,且能通過默認(rèn)值自動(dòng)推導(dǎo)出屬性的類型;同時(shí),它也能讓你充分利用默認(rèn)構(gòu)造器、構(gòu)造器繼承(后續(xù)章節(jié)將講到)等特性。

你可以使用更簡(jiǎn)單的方式在定義結(jié)構(gòu)體Fahrenheit時(shí)為屬性temperature設(shè)置默認(rèn)值:

    struct Fahrenheit {
        var temperature = 32.0
    }

定制化構(gòu)造過程

你可以通過輸入?yún)?shù)和可選屬性類型來定制構(gòu)造過程,也可以在構(gòu)造過程中修改常量屬性。這些都將在后面章節(jié)中提到。

構(gòu)造參數(shù)

你可以在定義構(gòu)造器時(shí)提供構(gòu)造參數(shù),為其提供定制化構(gòu)造所需值的類型和名字。構(gòu)造器參數(shù)的功能和語法跟函數(shù)和方法參數(shù)相同。

下面例子中定義了一個(gè)包含攝氏度溫度的結(jié)構(gòu)體Celsius。它定義了兩個(gè)不同的構(gòu)造器:init(fromFahrenheit:)init(fromKelvin:),二者分別通過接受不同刻度表示的溫度值來創(chuàng)建新的實(shí)例:

    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
        }
    }
    let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
    // boilingPointOfWater.temperatureInCelsius 是 100.0
    let freezingPointOfWater = Celsius(fromKelvin: 273.15)
    // freezingPointOfWater.temperatureInCelsius 是 0.0”

第一個(gè)構(gòu)造器擁有一個(gè)構(gòu)造參數(shù),其外部名字為fromFahrenheit,內(nèi)部名字為fahrenheit;第二個(gè)構(gòu)造器也擁有一個(gè)構(gòu)造參數(shù),其外部名字為fromKelvin,內(nèi)部名字為kelvin。這兩個(gè)構(gòu)造器都將唯一的參數(shù)值轉(zhuǎn)換成攝氏溫度值,并保存在屬性temperatureInCelsius中。

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

跟函數(shù)和方法參數(shù)相同,構(gòu)造參數(shù)也存在一個(gè)在構(gòu)造器內(nèi)部使用的參數(shù)名字和一個(gè)在調(diào)用構(gòu)造器時(shí)使用的外部參數(shù)名字。

然而,構(gòu)造器并不像函數(shù)和方法那樣在括號(hào)前有一個(gè)可辨別的名字。所以在調(diào)用構(gòu)造器時(shí),主要通過構(gòu)造器中的參數(shù)名和類型來確定需要調(diào)用的構(gòu)造器。正因?yàn)閰?shù)如此重要,如果你在定義構(gòu)造器時(shí)沒有提供參數(shù)的外部名字,Swift 會(huì)為每個(gè)構(gòu)造器的參數(shù)自動(dòng)生成一個(gè)跟內(nèi)部名字相同的外部名,就相當(dāng)于在每個(gè)構(gòu)造參數(shù)之前加了一個(gè)哈希符號(hào)。

注意:
如果你不希望為構(gòu)造器的某個(gè)參數(shù)提供外部名字,你可以使用下劃線_來顯示描述它的外部名,以此覆蓋上面所說的默認(rèn)行為。

以下例子中定義了一個(gè)結(jié)構(gòu)體Color,它包含了三個(gè)常量:red、greenblue。這些屬性可以存儲(chǔ)0.0到1.0之間的值,用來指示顏色中紅、綠、藍(lán)成分的含量。

Color提供了一個(gè)構(gòu)造器,其中包含三個(gè)Double類型的構(gòu)造參數(shù):

    struct Color {
        let red = 0.0, green = 0.0, blue = 0.0
        init(red: Double, green: Double, blue: Double) {
            self.red   = red
            self.green = green
            self.blue  = blue
        }
    }

每當(dāng)你創(chuàng)建一個(gè)新的Color實(shí)例,你都需要通過三種顏色的外部參數(shù)名來傳值,并調(diào)用構(gòu)造器。

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

注意,如果不通過外部參數(shù)名字傳值,你是沒法調(diào)用這個(gè)構(gòu)造器的。只要構(gòu)造器定義了某個(gè)外部參數(shù)名,你就必須使用它,忽略它將導(dǎo)致編譯錯(cuò)誤:

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

可選屬性類型

如果你定制的類型包含一個(gè)邏輯上允許取值為空的存儲(chǔ)型屬性--不管是因?yàn)樗鼰o法在初始化時(shí)賦值,還是因?yàn)樗梢栽谥竽硞€(gè)時(shí)間點(diǎn)可以賦值為空--你都需要將它定義為可選類型optional type??蛇x類型的屬性將自動(dòng)初始化為空nil,表示這個(gè)屬性是故意在初始化時(shí)設(shè)置為空的。

下面例子中定義了類SurveyQuestion,它包含一個(gè)可選字符串屬性response

    class SurveyQuestion {
        var text: String
        var response: String?
        init(text: String) {
            self.text = text
        }
        func ask() {
            println(text)
        }
    }
    let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
    cheeseQuestion.ask()
    // 輸出 "Do you like cheese?"
    cheeseQuestion.response = "Yes, I do like cheese."

調(diào)查問題在問題提出之后,我們才能得到回答。所以我們將屬性回答response聲明為String?類型,或者說是可選字符串類型optional String。當(dāng)SurveyQuestion實(shí)例化時(shí),它將自動(dòng)賦值為空nil,表明暫時(shí)還不存在此字符串。

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

只要在構(gòu)造過程結(jié)束前常量的值能確定,你可以在構(gòu)造過程中的任意時(shí)間點(diǎn)修改常量屬性的值。

注意:
對(duì)某個(gè)類實(shí)例來說,它的常量屬性只能在定義它的類的構(gòu)造過程中修改;不能在子類中修改。

你可以修改上面的SurveyQuestion示例,用常量屬性替代變量屬性text,指明問題內(nèi)容text在其創(chuàng)建之后不會(huì)再被修改。盡管text屬性現(xiàn)在是常量,我們?nèi)匀豢梢栽谄漕惖臉?gòu)造器中設(shè)置它的值:

    class SurveyQuestion {
        let text: String
        var response: String?
        init(text: String) {
            self.text = text
        }
        func ask() {
            println(text)
        }
    }
    let beetsQuestion = SurveyQuestion(text: "How about beets?")
    beetsQuestion.ask()
    // 輸出 "How about beets?"
    beetsQuestion.response = "I also like beets. (But not with cheese.)"

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

Swift 將為所有屬性已提供默認(rèn)值的且自身沒有定義任何構(gòu)造器的結(jié)構(gòu)體或基類,提供一個(gè)默認(rèn)的構(gòu)造器。這個(gè)默認(rèn)構(gòu)造器將簡(jiǎn)單的創(chuàng)建一個(gè)所有屬性值都設(shè)置為默認(rèn)值的實(shí)例。

下面例子中創(chuàng)建了一個(gè)類ShoppingListItem,它封裝了購物清單中的某一項(xiàng)的屬性:名字(name)、數(shù)量(quantity)和購買狀態(tài) purchase state。

    class ShoppingListItem {
        var name: String?
        var quantity = 1
        var purchased = false
    }
    var item = ShoppingListItem()

由于ShoppingListItem類中的所有屬性都有默認(rèn)值,且它是沒有父類的基類,它將自動(dòng)獲得一個(gè)可以為所有屬性設(shè)置默認(rèn)值的默認(rèn)構(gòu)造器(盡管代碼中沒有顯式為name屬性設(shè)置默認(rèn)值,但由于name是可選字符串類型,它將默認(rèn)設(shè)置為nil)。上面例子中使用默認(rèn)構(gòu)造器創(chuàng)造了一個(gè)ShoppingListItem類的實(shí)例(使用ShoppingListItem()形式的構(gòu)造器語法),并將其賦值給變量item

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

除上面提到的默認(rèn)構(gòu)造器,如果結(jié)構(gòu)體對(duì)所有存儲(chǔ)型屬性提供了默認(rèn)值且自身沒有提供定制的構(gòu)造器,它們能自動(dòng)獲得一個(gè)逐一成員構(gòu)造器。

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

下面例子中定義了一個(gè)結(jié)構(gòu)體Size,它包含兩個(gè)屬性widthheight。Swift 可以根據(jù)這兩個(gè)屬性的初始賦值0.0自動(dòng)推導(dǎo)出它們的類型Double

由于這兩個(gè)存儲(chǔ)型屬性都有默認(rèn)值,結(jié)構(gòu)體Size自動(dòng)獲得了一個(gè)逐一成員構(gòu)造器 init(width:height:)。 你可以用它來為Size創(chuàng)建新的實(shí)例:

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

值類型的構(gòu)造器代理

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

構(gòu)造器代理的實(shí)現(xiàn)規(guī)則和形式在值類型和類類型中有所不同。值類型(結(jié)構(gòu)體和枚舉類型)不支持繼承,所以構(gòu)造器代理的過程相對(duì)簡(jiǎn)單,因?yàn)樗鼈冎荒艽斫o本身提供的其它構(gòu)造器。類則不同,它可以繼承自其它類(請(qǐng)參考繼承),這意味著類有責(zé)任保證其所有繼承的存儲(chǔ)型屬性在構(gòu)造時(shí)也能正確的初始化。這些責(zé)任將在后續(xù)章節(jié)類的繼承和構(gòu)造過程中介紹。

對(duì)于值類型,你可以使用self.init在自定義的構(gòu)造器中引用其它的屬于相同值類型的構(gòu)造器。并且你只能在構(gòu)造器內(nèi)部調(diào)用self.init

注意,如果你為某個(gè)值類型定義了一個(gè)定制的構(gòu)造器,你將無法訪問到默認(rèn)構(gòu)造器(如果是結(jié)構(gòu)體,則無法訪問逐一對(duì)象構(gòu)造器)。這個(gè)限制可以防止你在為值類型定義了一個(gè)更復(fù)雜的,完成了重要準(zhǔn)備構(gòu)造器之后,別人還是錯(cuò)誤的使用了那個(gè)自動(dòng)生成的構(gòu)造器。

注意:
假如你想通過默認(rèn)構(gòu)造器、逐一對(duì)象構(gòu)造器以及你自己定制的構(gòu)造器為值類型創(chuàng)建實(shí)例,我們建議你將自己定制的構(gòu)造器寫到擴(kuò)展(extension)中,而不是跟值類型定義混在一起。想查看更多內(nèi)容,請(qǐng)查看擴(kuò)展章節(jié)。

下面例子將定義一個(gè)結(jié)構(gòu)體Rect,用來代表幾何矩形。這個(gè)例子需要兩個(gè)輔助的結(jié)構(gòu)體SizePoint,它們各自為其所有的屬性提供了初始值0.0。

    struct Size {
        var width = 0.0, height = 0.0
    }
    struct Point {
        var x = 0.0, y = 0.0
    }

你可以通過以下三種方式為Rect創(chuàng)建實(shí)例--使用默認(rèn)的0值來初始化originsize屬性;使用特定的originsize實(shí)例來初始化;使用特定的centersize來初始化。在下面Rect結(jié)構(gòu)體定義中,我們?yōu)檫@三種方式提供了三個(gè)自定義的構(gòu)造器:

    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è)Rect構(gòu)造器init(),在功能上跟沒有自定義構(gòu)造器時(shí)自動(dòng)獲得的默認(rèn)構(gòu)造器是一樣的。這個(gè)構(gòu)造器是一個(gè)空函數(shù),使用一對(duì)大括號(hào){}來描述,它沒有執(zhí)行任何定制的構(gòu)造過程。調(diào)用這個(gè)構(gòu)造器將返回一個(gè)Rect實(shí)例,它的originsize屬性都使用定義時(shí)的默認(rèn)值Point(x: 0.0, y: 0.0)Size(width: 0.0, height: 0.0)

    let basicRect = Rect()
    // basicRect 的原點(diǎn)是 (0.0, 0.0),尺寸是 (0.0, 0.0)

第二個(gè)Rect構(gòu)造器init(origin:size:),在功能上跟結(jié)構(gòu)體在沒有自定義構(gòu)造器時(shí)獲得的逐一成員構(gòu)造器是一樣的。這個(gè)構(gòu)造器只是簡(jiǎn)單地將originsize的參數(shù)值賦給對(duì)應(yīng)的存儲(chǔ)型屬性:

    let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
        size: Size(width: 5.0, height: 5.0))
    // originRect 的原點(diǎn)是 (2.0, 2.0),尺寸是 (5.0, 5.0)

第三個(gè)Rect構(gòu)造器init(center:size:)稍微復(fù)雜一點(diǎn)。它先通過centersize的值計(jì)算出origin的坐標(biāo)。然后再調(diào)用(或代理給)init(origin:size:)構(gòu)造器來將新的originsize值賦值到對(duì)應(yīng)的屬性中:

    let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
        size: Size(width: 3.0, height: 3.0))
    // centerRect 的原點(diǎn)是 (2.5, 2.5),尺寸是 (3.0, 3.0)

構(gòu)造器init(center:size:)可以自己將originsize的新值賦值到對(duì)應(yīng)的屬性中。然而盡量利用現(xiàn)有的構(gòu)造器和它所提供的功能來實(shí)現(xiàn)init(center:size:)的功能,是更方便、更清晰和更直觀的方法。

注意:
如果你想用另外一種不需要自己定義init()init(origin:size:)的方式來實(shí)現(xiàn)這個(gè)例子,請(qǐng)參考擴(kuò)展。

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

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

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

指定構(gòu)造器和便利構(gòu)造器

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

每一個(gè)類都必須擁有至少一個(gè)指定構(gòu)造器。在某些情況下,許多類通過繼承了父類中的指定構(gòu)造器而滿足了這個(gè)條件。具體內(nèi)容請(qǐng)參考后續(xù)章節(jié)自動(dòng)構(gòu)造器的繼承

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

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

構(gòu)造器鏈

為了簡(jiǎn)化指定構(gòu)造器和便利構(gòu)造器之間的調(diào)用關(guān)系,Swift 采用以下三條規(guī)則來限制構(gòu)造器之間的代理調(diào)用:

規(guī)則 1

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

規(guī)則 2

便利構(gòu)造器必須調(diào)用同一類中定義的其它構(gòu)造器。

規(guī)則 3

便利構(gòu)造器必須最終以調(diào)用一個(gè)指定構(gòu)造器結(jié)束。

一個(gè)更方便記憶的方法是:

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

這些規(guī)則可以通過下面圖例來說明:

http://wiki.jikexueyuan.com/project/swift-language-guide/images/Initialization_1.png" alt="Image of Initialization_1.png" />

如圖所示,父類中包含一個(gè)指定構(gòu)造器和兩個(gè)便利構(gòu)造器。其中一個(gè)便利構(gòu)造器調(diào)用了另外一個(gè)便利構(gòu)造器,而后者又調(diào)用了唯一的指定構(gòu)造器。這滿足了上面提到的規(guī)則2和3。這個(gè)父類沒有自己的父類,所以規(guī)則1沒有用到。

子類中包含兩個(gè)指定構(gòu)造器和一個(gè)便利構(gòu)造器。便利構(gòu)造器必須調(diào)用兩個(gè)指定構(gòu)造器中的任意一個(gè),因?yàn)樗荒苷{(diào)用同一個(gè)類里的其他構(gòu)造器。這滿足了上面提到的規(guī)則2和3。而兩個(gè)指定構(gòu)造器必須調(diào)用父類中唯一的指定構(gòu)造器,這滿足了規(guī)則1。

注意:
這些規(guī)則不會(huì)影響使用時(shí),如何用類去創(chuàng)建實(shí)例。任何上圖中展示的構(gòu)造器都可以用來完整創(chuàng)建對(duì)應(yīng)類的實(shí)例。這些規(guī)則只在實(shí)現(xiàn)類的定義時(shí)有影響。

下面圖例中展示了一種針對(duì)四個(gè)類的更復(fù)雜的類層級(jí)結(jié)構(gòu)。它演示了指定構(gòu)造器是如何在類層級(jí)中充當(dāng)“管道”的作用,在類的構(gòu)造器鏈上簡(jiǎn)化了類之間的相互關(guān)系。

http://wiki.jikexueyuan.com/project/swift-language-guide/images/Initialization_2.png" alt="Image of Initialization_2.png" />

兩段式構(gòu)造過程

Swift 中類的構(gòu)造過程包含兩個(gè)階段。第一個(gè)階段,每個(gè)存儲(chǔ)型屬性通過引入它們的類的構(gòu)造器來設(shè)置初始值。當(dāng)每一個(gè)存儲(chǔ)型屬性值被確定后,第二階段開始,它給每個(gè)類一次機(jī)會(huì)在新實(shí)例準(zhǔn)備使用之前進(jìn)一步定制它們的存儲(chǔ)型屬性。

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

注意:
Swift的兩段式構(gòu)造過程跟 Objective-C 中的構(gòu)造過程類似。最主要的區(qū)別在于階段 1,Objective-C 給每一個(gè)屬性賦值0或空值(比如說0nil)。Swift 的構(gòu)造流程則更加靈活,它允許你設(shè)置定制的初始值,并自如應(yīng)對(duì)某些屬性不能以0nil作為合法默認(rèn)值的情況。

Swift 編譯器將執(zhí)行 4 種有效的安全檢查,以確保兩段式構(gòu)造過程能順利完成:

安全檢查 1

指定構(gòu)造器必須保證它所在類引入的所有屬性都必須先初始化完成,之后才能將其它構(gòu)造任務(wù)向上代理給父類中的構(gòu)造器。

如上所述,一個(gè)對(duì)象的內(nèi)存只有在其所有存儲(chǔ)型屬性確定之后才能完全初始化。為了滿足這一規(guī)則,指定構(gòu)造器必須保證它所在類引入的屬性在它往上代理之前先完成初始化。

安全檢查 2

指定構(gòu)造器必須先向上代理調(diào)用父類構(gòu)造器,然后再為繼承的屬性設(shè)置新值。如果沒這么做,指定構(gòu)造器賦予的新值將被父類中的構(gòu)造器所覆蓋。

安全檢查 3

便利構(gòu)造器必須先代理調(diào)用同一類中的其它構(gòu)造器,然后再為任意屬性賦新值。如果沒這么做,便利構(gòu)造器賦予的新值將被同一類中其它指定構(gòu)造器所覆蓋。

安全檢查 4

構(gòu)造器在第一階段構(gòu)造完成之前,不能調(diào)用任何實(shí)例方法、不能讀取任何實(shí)例屬性的值,self的值不能被引用。

類實(shí)例在第一階段結(jié)束以前并不是完全有效,僅能訪問屬性和調(diào)用方法,一旦完成第一階段,該實(shí)例才會(huì)聲明為有效實(shí)例。

以下是兩段式構(gòu)造過程中基于上述安全檢查的構(gòu)造流程展示:

階段 1

  • 某個(gè)指定構(gòu)造器或便利構(gòu)造器被調(diào)用;
  • 完成新實(shí)例內(nèi)存的分配,但此時(shí)內(nèi)存還沒有被初始化;
  • 指定構(gòu)造器確保其所在類引入的所有存儲(chǔ)型屬性都已賦初值。存儲(chǔ)型屬性所屬的內(nèi)存完成初始化;
  • 指定構(gòu)造器將調(diào)用父類的構(gòu)造器,完成父類屬性的初始化;
  • 這個(gè)調(diào)用父類構(gòu)造器的過程沿著構(gòu)造器鏈一直往上執(zhí)行,直到到達(dá)構(gòu)造器鏈的最頂部;
  • 當(dāng)?shù)竭_(dá)了構(gòu)造器鏈最頂部,且已確保所有實(shí)例包含的存儲(chǔ)型屬性都已經(jīng)賦值,這個(gè)實(shí)例的內(nèi)存被認(rèn)為已經(jīng)完全初始化。此時(shí)階段1完成。

階段 2

  • 從頂部構(gòu)造器鏈一直往下,每個(gè)構(gòu)造器鏈中類的指定構(gòu)造器都有機(jī)會(huì)進(jìn)一步定制實(shí)例。構(gòu)造器此時(shí)可以訪問self、修改它的屬性并調(diào)用實(shí)例方法等等。
  • 最終,任意構(gòu)造器鏈中的便利構(gòu)造器可以有機(jī)會(huì)定制實(shí)例和使用self。

下圖展示了在假定的子類和父類之間構(gòu)造的階段1:

http://wiki.jikexueyuan.com/project/swift-language-guide/images/Initialization_3.png" alt="Image of Initialization_3.png" />

在這個(gè)例子中,構(gòu)造過程從對(duì)子類中一個(gè)便利構(gòu)造器的調(diào)用開始。這個(gè)便利構(gòu)造器此時(shí)沒法修改任何屬性,它把構(gòu)造任務(wù)代理給同一類中的指定構(gòu)造器。

如安全檢查1所示,指定構(gòu)造器將確保所有子類的屬性都有值。然后它將調(diào)用父類的指定構(gòu)造器,并沿著造器鏈一直往上完成父類的構(gòu)建過程。

父類中的指定構(gòu)造器確保所有父類的屬性都有值。由于沒有更多的父類需要構(gòu)建,也就無需繼續(xù)向上做構(gòu)建代理。

一旦父類中所有屬性都有了初始值,實(shí)例的內(nèi)存被認(rèn)為是完全初始化,而階段1也已完成。

以下展示了相同構(gòu)造過程的階段2:

http://wiki.jikexueyuan.com/project/swift-language-guide/images/Initialization_4.png" alt="Image of Initialization_4.png" />

父類中的指定構(gòu)造器現(xiàn)在有機(jī)會(huì)進(jìn)一步來定制實(shí)例(盡管它沒有這種必要)。

一旦父類中的指定構(gòu)造器完成調(diào)用,子類的構(gòu)指定構(gòu)造器可以執(zhí)行更多的定制操作(同樣,它也沒有這種必要)。

最終,一旦子類的指定構(gòu)造器完成調(diào)用,最開始被調(diào)用的便利構(gòu)造器可以執(zhí)行更多的定制操作。

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

跟 Objective-C 中的子類不同,Swift 中的子類不會(huì)默認(rèn)繼承父類的構(gòu)造器。Swift 的這種機(jī)制可以防止一個(gè)父類的簡(jiǎn)單構(gòu)造器被一個(gè)更專業(yè)的子類繼承,并被錯(cuò)誤的用來創(chuàng)建子類的實(shí)例。

假如你希望自定義的子類中能實(shí)現(xiàn)一個(gè)或多個(gè)跟父類相同的構(gòu)造器--也許是為了完成一些定制的構(gòu)造過程--你可以在你定制的子類中提供和重載與父類相同的構(gòu)造器。

如果你重載的構(gòu)造器是一個(gè)指定構(gòu)造器,你可以在子類里重載它的實(shí)現(xiàn),并在自定義版本的構(gòu)造器中調(diào)用父類版本的構(gòu)造器。

如果你重載的構(gòu)造器是一個(gè)便利構(gòu)造器,你的重載過程必須通過調(diào)用同一類中提供的其它指定構(gòu)造器來實(shí)現(xiàn)。這一規(guī)則的詳細(xì)內(nèi)容請(qǐng)參考構(gòu)造器鏈。

注意:
與方法、屬性和下標(biāo)不同,在重載構(gòu)造器時(shí)你沒有必要使用關(guān)鍵字override

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

如上所述,子類不會(huì)默認(rèn)繼承父類的構(gòu)造器。但是如果特定條件可以滿足,父類構(gòu)造器是可以被自動(dòng)繼承的。在實(shí)踐中,這意味著對(duì)于許多常見場(chǎng)景你不必重載父類的構(gòu)造器,并且在盡可能安全的情況下以最小的代價(jià)來繼承父類的構(gòu)造器。

假設(shè)要為子類中引入的任意新屬性提供默認(rèn)值,請(qǐng)遵守以下2個(gè)規(guī)則:

規(guī)則 1

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

規(guī)則 2

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

即使你在子類中添加了更多的便利構(gòu)造器,這兩條規(guī)則仍然適用。

注意:
子類可以通過部分滿足規(guī)則2的方式,使用子類便利構(gòu)造器來實(shí)現(xiàn)父類的指定構(gòu)造器。

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

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

    init(parameters) {
        statements
    }

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

    convenience init(parameters) {
        statements
    }

指定構(gòu)造器和便利構(gòu)造器實(shí)戰(zhàn)

接下來的例子將在實(shí)戰(zhàn)中展示指定構(gòu)造器、便利構(gòu)造器和自動(dòng)構(gòu)造器的繼承。它定義了包含三個(gè)類FoodRecipeIngredient以及ShoppingListItem的類層次結(jié)構(gòu),并將演示它們的構(gòu)造器是如何相互作用的。

類層次中的基類是Food,它是一個(gè)簡(jiǎn)單的用來封裝食物名字的類。Food類引入了一個(gè)叫做nameString類型屬性,并且提供了兩個(gè)構(gòu)造器來創(chuàng)建Food實(shí)例:

    class Food {
        var name: String
        init(name: String) {
            self.name = name
        }
        convenience init() {
            self.init(name: "[Unnamed]")
        }
    }

下圖中展示了Food的構(gòu)造器鏈:

http://wiki.jikexueyuan.com/project/swift-language-guide/images/Initialization_5.png" alt="Image of Initialization_5.png" />

類沒有提供一個(gè)默認(rèn)的逐一成員構(gòu)造器,所以Food類提供了一個(gè)接受單一參數(shù)name的指定構(gòu)造器。這個(gè)構(gòu)造器可以使用一個(gè)特定的名字來創(chuàng)建新的Food實(shí)例:

    let namedMeat = Food(name: "Bacon")
    // namedMeat 的名字是 "Bacon”

Food類中的構(gòu)造器init(name: String)被定義為一個(gè)指定構(gòu)造器,因?yàn)樗艽_保所有新Food實(shí)例的中存儲(chǔ)型屬性都被初始化。Food類沒有父類,所以init(name: String)構(gòu)造器不需要調(diào)用super.init()來完成構(gòu)造。

Food類同樣提供了一個(gè)沒有參數(shù)的便利構(gòu)造器 init()。這個(gè)init()構(gòu)造器為新食物提供了一個(gè)默認(rèn)的占位名字,通過代理調(diào)用同一類中定義的指定構(gòu)造器init(name: String)并給參數(shù)name傳值[Unnamed]來實(shí)現(xiàn):

    let mysteryMeat = Food()
    // mysteryMeat 的名字是 [Unnamed]

類層級(jí)中的第二個(gè)類是Food的子類RecipeIngredient。RecipeIngredient類構(gòu)建了食譜中的一味調(diào)味劑。它引入了Int類型的數(shù)量屬性quantity(以及從Food繼承過來的name屬性),并且定義了兩個(gè)構(gòu)造器來創(chuàng)建RecipeIngredient實(shí)例:

    class RecipeIngredient: Food {
        var quantity: Int
        init(name: String, quantity: Int) {
            self.quantity = quantity
            super.init(name: name)
        }
        override convenience init(name: String) {
            self.init(name: name, quantity: 1)
        }
    }

下圖中展示了RecipeIngredient類的構(gòu)造器鏈:

http://wiki.jikexueyuan.com/project/swift-language-guide/images/Initialization_6.png" alt="Image of Initialization_6.png" />

RecipeIngredient類擁有一個(gè)指定構(gòu)造器init(name: String, quantity: Int),它可以用來產(chǎn)生新RecipeIngredient實(shí)例的所有屬性值。這個(gè)構(gòu)造器一開始先將傳入的quantity參數(shù)賦值給quantity屬性,這個(gè)屬性也是唯一在RecipeIngredient中新引入的屬性。隨后,構(gòu)造器將任務(wù)向上代理給父類Foodinit(name: String)。這個(gè)過程滿足兩段式構(gòu)造過程中的安全檢查1。

RecipeIngredient也定義了一個(gè)便利構(gòu)造器init(name: String),它只通過name來創(chuàng)建RecipeIngredient的實(shí)例。這個(gè)便利構(gòu)造器假設(shè)任意RecipeIngredient實(shí)例的quantity為1,所以不需要顯示指明數(shù)量即可創(chuàng)建出實(shí)例。這個(gè)便利構(gòu)造器的定義可以讓創(chuàng)建實(shí)例更加方便和快捷,并且避免了使用重復(fù)的代碼來創(chuàng)建多個(gè)quantity為 1 的RecipeIngredient實(shí)例。這個(gè)便利構(gòu)造器只是簡(jiǎn)單的將任務(wù)代理給了同一類里提供的指定構(gòu)造器。

注意,RecipeIngredient的便利構(gòu)造器init(name: String)使用了跟Food中指定構(gòu)造器init(name: String)相同的參數(shù)。因?yàn)檫@個(gè)便利構(gòu)造器重寫要父類的指定構(gòu)造器init(name: String),必須在前面使用使用override標(biāo)識(shí)。

在這個(gè)例子中,RecipeIngredient的父類是Food,它有一個(gè)便利構(gòu)造器init()。這個(gè)構(gòu)造器因此也被RecipeIngredient繼承。這個(gè)繼承的init()函數(shù)版本跟Food提供的版本是一樣的,除了它是將任務(wù)代理給RecipeIngredient版本的init(name: String)而不是Food提供的版本。

所有的這三種構(gòu)造器都可以用來創(chuàng)建新的RecipeIngredient實(shí)例:

    let oneMysteryItem = RecipeIngredient()
    let oneBacon = RecipeIngredient(name: "Bacon")
    let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)

類層級(jí)中第三個(gè)也是最后一個(gè)類是RecipeIngredient的子類,叫做ShoppingListItem。這個(gè)類構(gòu)建了購物單中出現(xiàn)的某一種調(diào)味料。

購物單中的每一項(xiàng)總是從unpurchased未購買狀態(tài)開始的。為了展現(xiàn)這一事實(shí),ShoppingListItem引入了一個(gè)布爾類型的屬性purchased,它的默認(rèn)值是falseShoppingListItem還添加了一個(gè)計(jì)算型屬性description,它提供了關(guān)于ShoppingListItem實(shí)例的一些文字描述:

    class ShoppingListItem: RecipeIngredient {
        var purchased = false
        var description: String {
        var output = "\(quantity) x \(name.lowercaseString)"
            output += purchased ? " ?" : " ?"
            return output
        }
    }

注意:
ShoppingListItem沒有定義構(gòu)造器來為purchased提供初始化值,這是因?yàn)槿魏翁砑拥劫徫飭蔚捻?xiàng)的初始狀態(tài)總是未購買。

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

下圖種展示了所有三個(gè)類的構(gòu)造器鏈:

http://wiki.jikexueyuan.com/project/swift-language-guide/images/Initialization_7.png" alt="Image of Initialization_7.png" />

你可以使用全部三個(gè)繼承來的構(gòu)造器來創(chuàng)建ShoppingListItem的新實(shí)例:

    var breakfastList = [
        ShoppingListItem(),
        ShoppingListItem(name: "Bacon"),
        ShoppingListItem(name: "Eggs", quantity: 6),
    ]
    breakfastList[0].name = "Orange juice"
    breakfastList[0].purchased = true
    for item in breakfastList {
        println(item.description)
    }
    // 1 x orange juice ?
    // 1 x bacon ?
    // 6 x eggs ?

如上所述,例子中通過字面量方式創(chuàng)建了一個(gè)新數(shù)組breakfastList,它包含了三個(gè)新的ShoppingListItem實(shí)例,因此數(shù)組的類型也能自動(dòng)推導(dǎo)為ShoppingListItem[]。在數(shù)組創(chuàng)建完之后,數(shù)組中第一個(gè)ShoppingListItem實(shí)例的名字從[Unnamed]修改為Orange juice,并標(biāo)記為已購買。接下來通過遍歷數(shù)組每個(gè)元素并打印它們的描述值,展示了所有項(xiàng)當(dāng)前的默認(rèn)狀態(tài)都已按照預(yù)期完成了賦值。

可失敗構(gòu)造器

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

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

注意:

可失敗構(gòu)造器的參數(shù)名和參數(shù)類型,不能與其它非可失敗構(gòu)造器的參數(shù)名,及其類型相同。

可失敗構(gòu)造器,在構(gòu)建對(duì)象的過程中,創(chuàng)建一個(gè)其自身類型為可選類型的對(duì)象。你通過return nil 語句,來表明可失敗構(gòu)造器在何種情況下“失敗”。

注意:

嚴(yán)格來說,構(gòu)造器都不支持返回值。因?yàn)闃?gòu)造器本身的作用,只是為了能確保對(duì)象自身能被正確構(gòu)建。所以即使你在表明可失敗構(gòu)造器,失敗的這種情況下,用到了return nil。也不要在表明可失敗構(gòu)造器成功的這種情況下,使用關(guān)鍵字 return

下例中,定義了一個(gè)名為Animal的結(jié)構(gòu)體,其中有一個(gè)名為species的,String類型的常量屬性。同時(shí)該結(jié)構(gòu)體還定義了一個(gè),帶一個(gè)String類型參數(shù)species的,可失敗構(gòu)造器。這個(gè)可失敗構(gòu)造器,被用來檢查傳入的參數(shù)是否為一個(gè)空字符串,如果為空字符串,則該可失敗構(gòu)造器,構(gòu)建對(duì)象失敗,否則成功。

    struct Animal {
        let species: String
        init?(species: String) {
            if species.isEmpty { return nil }
            self.species = species
        }
    }

你可以通過該可失敗構(gòu)造器來構(gòu)建一個(gè)Animal的對(duì)象,并檢查其構(gòu)建過程是否成功。

    let someCreature = Animal(species: "Giraffe")
    // someCreature 的類型是 Animal? 而不是 Animal
    if let giraffe = someCreature {
        println("An animal was initialized with a species of \(giraffe.species)")
    }
    // 打印 "An animal was initialized with a species of Giraffe"

如果你給該可失敗構(gòu)造器傳入一個(gè)空字符串作為其參數(shù),則該可失敗構(gòu)造器失敗。

    let anonymousCreature = Animal(species: "")
    // anonymousCreature 的類型是 Animal?, 而不是 Animal
    if anonymousCreature == nil {
        println("The anonymous creature could not be initialized")
    }
    // 打印 "The anonymous creature could not be initialized"

注意:

空字符串("")和一個(gè)值為nil的可選類型的字符串是兩個(gè)完全不同的概念。上例中的空字符串("")其實(shí)是一個(gè)有效的,非可選類型的字符串。這里我們只所以讓Animal的可失敗構(gòu)造器,構(gòu)建對(duì)象失敗,只是因?yàn)閷?duì)于Animal這個(gè)類的species屬性來說,它更適合有一個(gè)具體的值,而不是空字符串。

枚舉類型的可失敗構(gòu)造器

你可以通過構(gòu)造一個(gè)帶一個(gè)或多個(gè)參數(shù)的可失敗構(gòu)造器來獲取枚舉類型中特定的枚舉成員。還能在參數(shù)不滿足你所期望的條件時(shí),導(dǎo)致構(gòu)造失敗。

下例中,定義了一個(gè)名為TemperatureUnit的枚舉類型。其中包含了三個(gè)可能的枚舉成員(Kelvin,Celsius,和 Fahrenheit)和一個(gè)被用來找到Character值所對(duì)應(yīng)的枚舉成員的可失敗構(gòu)造器:

    enum TemperatureUnit {
        case Kelvin, Celsius, Fahrenheit
        init?(symbol: Character) {
            switch symbol {
            case "K":
                self = .Kelvin
            case "C":
                self = .Celsius
            case "F":
                self = .Fahrenheit
            default:
                return nil
            }
        }
    }

你可以通過給該可失敗構(gòu)造器傳遞合適的參數(shù)來獲取這三個(gè)枚舉成員中相匹配的其中一個(gè)枚舉成員。當(dāng)參數(shù)的值不能與任意一枚舉成員相匹配時(shí),該枚舉類型的構(gòu)建過程失敗:

    let fahrenheitUnit = TemperatureUnit(symbol: "F")
    if fahrenheitUnit != nil {
        println("This is a defined temperature unit, so initialization succeeded.")
    }
    // 打印 "This is a defined temperature unit, so initialization succeeded."
    let unknownUnit = TemperatureUnit(symbol: "X")
    if unknownUnit == nil {
        println("This is not a defined temperature unit, so initialization failed.")
    }
    // 打印 "This is not a defined temperature unit, so initialization failed."

帶原始值的枚舉類型的可失敗構(gòu)造器

帶原始值的枚舉類型會(huì)自帶一個(gè)可失敗構(gòu)造器init?(rawValue:),該可失敗構(gòu)造器有一個(gè)名為rawValue的默認(rèn)參數(shù),其類型和枚舉類型的原始值類型一致,如果該參數(shù)的值能夠和枚舉類型成員所帶的原始值匹配,則該構(gòu)造器構(gòu)造一個(gè)帶此原始值的枚舉成員,否則構(gòu)造失敗。

因此上面的 TemperatureUnit的例子可以重寫為:

    enum TemperatureUnit: Character {
        case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
    }
    let fahrenheitUnit = TemperatureUnit(rawValue: "F")
    if fahrenheitUnit != nil {
        println("This is a defined temperature unit, so initialization succeeded.")
    }
    // prints "This is a defined temperature unit, so initialization succeeded."
    let unknownUnit = TemperatureUnit(rawValue: "X")
    if unknownUnit == nil {
        println("This is not a defined temperature unit, so initialization failed.")
    }
    // prints "This is not a defined temperature unit, so initialization failed."

類的可失敗構(gòu)造器

值類型(如結(jié)構(gòu)體或枚舉類型)的可失敗構(gòu)造器,對(duì)何時(shí)何地觸發(fā)構(gòu)造失敗這個(gè)行為沒有任何的限制。比如在前面的例子中,結(jié)構(gòu)體Animal的可失敗構(gòu)造器觸發(fā)失敗的行為,甚至發(fā)生在species屬性的值被初始化以前。而對(duì)類而言,就沒有那么幸運(yùn)了。類的可失敗構(gòu)造器只能在所有的類屬性被初始化后和所有類之間的構(gòu)造器之間的代理調(diào)用發(fā)生完后觸發(fā)失敗行為。

下例子中,定義了一個(gè)名為Product的類,其內(nèi)部結(jié)構(gòu)和結(jié)構(gòu)體Animal很相似,內(nèi)部也有一個(gè)名為nameString類型的屬性。由于該屬性的值同樣不能為空字符串,所以我們加入了可失敗構(gòu)造器來確保該類滿足上述條件。但由于Product類不是一個(gè)結(jié)構(gòu)體,所以當(dāng)想要在該類中添加可失敗構(gòu)造器觸發(fā)失敗條件時(shí),必須確保name屬性被初始化。因此我們把name屬性的String類型做了一點(diǎn)點(diǎn)小小的修改,把其改為隱式解析可選類型(String!),來確??墒?gòu)造器觸發(fā)失敗條件時(shí),所有類屬性都被初始化了。因?yàn)樗锌蛇x類型都有一個(gè)默認(rèn)的初始值nil。因此最后Product類可寫為:

    class Product {
        let name: String!
        init?(name: String) {
            if name.isEmpty { return nil }
            self.name = name
        }
    }

因?yàn)?code>name屬性是一個(gè)常量,所以一旦Product類構(gòu)造成功,name屬性肯定有一個(gè)非nil的值。因此完全可以放心大膽的直接訪問Product類的name屬性,而不用考慮去檢查name屬性是否有值。

    if let bowTie = Product(name: "bow tie") {
        // 不需要檢查 bowTie.name == nil
        println("The product's name is \(bowTie.name)")
    }
    // 打印 "The product's name is bow tie"

構(gòu)造失敗的傳遞

可失敗構(gòu)造器同樣滿足在構(gòu)造器鏈中所描述的構(gòu)造規(guī)則。其允許在同一類,結(jié)構(gòu)體和枚舉中橫向代理其他的可失敗構(gòu)造器。類似的,子類的可失敗構(gòu)造器也能向上代理基類的可失敗構(gòu)造器。

無論是向上代理還是橫向代理,如果你代理的可失敗構(gòu)造器,在構(gòu)造過程中觸發(fā)了構(gòu)造失敗的行為,整個(gè)構(gòu)造過程都將被立即終止,接下來任何的構(gòu)造代碼都將不會(huì)被執(zhí)行。

注意:

可失敗構(gòu)造器也可以代理調(diào)用其它的非可失敗構(gòu)造器。通過這個(gè)方法,你可以為已有的構(gòu)造過程加入構(gòu)造失敗的條件。

下面這個(gè)例子,定義了一個(gè)名為CartItemProduct類的子類。這個(gè)類建立了一個(gè)在線購物車中的物品的模型,它有一個(gè)名為quantity的常量參數(shù),用來表示該物品的數(shù)量至少為1:

    class CartItem: Product {
        let quantity: Int!
        init?(name: String, quantity: Int) {
            super.init(name: name)
            if quantity < 1 { return nil }
            self.quantity = quantity
        }
    }

Product類中的name屬性相類似的,CartItem類中的quantity屬性的類型也是一個(gè)隱式解析可選類型,只不過由(String!)變?yōu)榱耍?code>Int!)。這樣做都是為了確保在構(gòu)造過程中,該屬性在被賦予特定的值之前能有一個(gè)默認(rèn)的初始值nil。

可失敗構(gòu)造器總是先向上代理調(diào)用基類,Product的構(gòu)造器 init(name:)。這滿足了可失敗構(gòu)造器在觸發(fā)構(gòu)造失敗這個(gè)行為前必須總是執(zhí)行構(gòu)造代理調(diào)用這個(gè)條件。

如果由于name的值為空而導(dǎo)致基類的構(gòu)造器在構(gòu)造過程中失敗。則整個(gè)CartIem類的構(gòu)造過程都將失敗,后面的子類的構(gòu)造過程都將不會(huì)被執(zhí)行。如果基類構(gòu)建成功,則繼續(xù)運(yùn)行子類的構(gòu)造器代碼。

如果你構(gòu)造了一個(gè)CartItem對(duì)象,并且該對(duì)象的name屬性不為空以及quantity屬性為1或者更多,則構(gòu)造成功:

    if let twoSocks = CartItem(name: "sock", quantity: 2) {
        println("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
    }
    // 打印 "Item: sock, quantity: 2"

如果你構(gòu)造一個(gè)CartItem對(duì)象,其quantity的值0, 則CartItem的可失敗構(gòu)造器觸發(fā)構(gòu)造失敗的行為:

    if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
        println("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
    } else {
        println("Unable to initialize zero shirts")
    }
    // 打印 "Unable to initialize zero shirts"

類似的, 如果你構(gòu)造一個(gè)CartItem對(duì)象,但其name的值為空, 則基類Product的可失敗構(gòu)造器將觸發(fā)構(gòu)造失敗的行為,整個(gè)CartItem的構(gòu)造行為同樣為失?。?/p>

    if let oneUnnamed = CartItem(name: "", quantity: 1) {
        println("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
    } else {
        println("Unable to initialize one unnamed product")
    }
    // 打印 "Unable to initialize one unnamed product"

覆蓋一個(gè)可失敗構(gòu)造器

就如同其它構(gòu)造器一樣,你也可以用子類的可失敗構(gòu)造器覆蓋基類的可失敗構(gòu)造器?;蛘吣阋部梢杂米宇惖姆强墒?gòu)造器覆蓋一個(gè)基類的可失敗構(gòu)造器。這樣做的好處是,即使基類的構(gòu)造器為可失敗構(gòu)造器,但當(dāng)子類的構(gòu)造器在構(gòu)造過程不可能失敗時(shí),我們也可以把它修改過來。

注意當(dāng)你用一個(gè)子類的非可失敗構(gòu)造器覆蓋了一個(gè)父類的可失敗構(gòu)造器時(shí),子類的構(gòu)造器將不再能向上代理父類的可失敗構(gòu)造器。一個(gè)非可失敗的構(gòu)造器永遠(yuǎn)也不能代理調(diào)用一個(gè)可失敗構(gòu)造器。

注意:

你可以用一個(gè)非可失敗構(gòu)造器覆蓋一個(gè)可失敗構(gòu)造器,但反過來卻行不通。

下例定義了一個(gè)名為Document的類,這個(gè)類中的name屬性允許為nil和一個(gè)非空字符串,但不能是一個(gè)空字符串:

    class Document {
        var name: String?
        // 該構(gòu)造器構(gòu)建了一個(gè)name屬性值為nil的document對(duì)象
        init() {}
        // 該構(gòu)造器構(gòu)建了一個(gè)name屬性值為非空字符串的document對(duì)象
        init?(name: String) {
            if name.isEmpty { return nil }
            self.name = name
        }
    }

下面這個(gè)例子,定義了一個(gè)名為AutomaticallyNamedDocumentDocument類的子類。這個(gè)子類覆蓋了基類的兩個(gè)指定構(gòu)造器。確保了不論在何種情況下name屬性總是有一個(gè)非空字符串[Untitled]的值。

    class AutomaticallyNamedDocument: Document {
        override init() {
            super.init()
            self.name = "[Untitled]"
        }
        override init(name: String) {
            super.init()
            if name.isEmpty {
                self.name = "[Untitled]"
            } else {
                self.name = name
            }
        }
    }

AutomaticallyNamedDocument用一個(gè)非可失敗構(gòu)造器init(name:),覆蓋了基類的可失敗構(gòu)造器init?(name:)。因?yàn)樽宇愑貌煌姆椒ㄌ幚砹?code>name屬性的值為一個(gè)空字符串的這種情況。所以子類將不再需要一個(gè)可失敗的構(gòu)造器。

可失敗構(gòu)造器 init!

通常來說我們通過在init關(guān)鍵字后添加問號(hào)的方式來定義一個(gè)可失敗構(gòu)造器,但你也可以使用通過在init后面添加驚嘆號(hào)的方式來定義一個(gè)可失敗構(gòu)造器(init!),該可失敗構(gòu)造器將會(huì)構(gòu)建一個(gè)特定類型的隱式解析可選類型的對(duì)象。

你可以在 init?構(gòu)造器中代理調(diào)用 init!構(gòu)造器,反之亦然。 你也可以用 init?覆蓋 init!,反之亦然。 你還可以用 init代理調(diào)用init!,但這會(huì)觸發(fā)一個(gè)斷言:是否 init!構(gòu)造器會(huì)觸發(fā)構(gòu)造失敗?

必要構(gòu)造器

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

    class SomeClass {
        required init() {
            // 在這里添加該必要構(gòu)造器的實(shí)現(xiàn)代碼
        }
    }

當(dāng)子類覆蓋基類的必要構(gòu)造器時(shí),必須在子類的構(gòu)造器前同樣添加required修飾符以確保當(dāng)其它類繼承該子類時(shí),該構(gòu)造器同為必要構(gòu)造器。在覆蓋基類的必要構(gòu)造器時(shí),不需要添加override修飾符:

    class SomeSubclass: SomeClass {
        required init() {
            // 在這里添加子類必要構(gòu)造器的實(shí)現(xiàn)代碼
        }
    }

注意:

如果子類繼承的構(gòu)造器能滿足必要構(gòu)造器的需求,則你無需顯示的在子類中提供必要構(gòu)造器的實(shí)現(xiàn)。

通過閉包和函數(shù)來設(shè)置屬性的默認(rèn)值

如果某個(gè)存儲(chǔ)型屬性的默認(rèn)值需要特別的定制或準(zhǔn)備,你就可以使用閉包或全局函數(shù)來為其屬性提供定制的默認(rèn)值。每當(dāng)某個(gè)屬性所屬的新類型實(shí)例創(chuàng)建時(shí),對(duì)應(yīng)的閉包或函數(shù)會(huì)被調(diào)用,而它們的返回值會(huì)當(dāng)做默認(rèn)值賦值給這個(gè)屬性。

這種類型的閉包或函數(shù)一般會(huì)創(chuàng)建一個(gè)跟屬性類型相同的臨時(shí)變量,然后修改它的值以滿足預(yù)期的初始狀態(tài),最后將這個(gè)臨時(shí)變量的值作為屬性的默認(rèn)值進(jìn)行返回。

下面列舉了閉包如何提供默認(rèn)值的代碼概要:

    class SomeClass {
    let someProperty: SomeType = {
            // 在這個(gè)閉包中給 someProperty 創(chuàng)建一個(gè)默認(rèn)值
            // someValue 必須和 SomeType 類型相同
            return someValue
            }()
    }

注意閉包結(jié)尾的大括號(hào)后面接了一對(duì)空的小括號(hào)。這是用來告訴 Swift 需要立刻執(zhí)行此閉包。如果你忽略了這對(duì)括號(hào),相當(dāng)于是將閉包本身作為值賦值給了屬性,而不是將閉包的返回值賦值給屬性。

注意:
如果你使用閉包來初始化屬性的值,請(qǐng)記住在閉包執(zhí)行時(shí),實(shí)例的其它部分都還沒有初始化。這意味著你不能夠在閉包里訪問其它的屬性,就