Swift學習筆記十三

初始化數組

初始化是一個在類、結構體或枚舉的實例對象建立以前,對它進行預處理的過程,包括給那個對象的每個存儲式屬性設定初始值,以及進行一些其餘的準備操做。安全

經過定義初始化器(initializer)來實現初始化過程,它就像一種在建立該類型實例對象時會自動調用的方法。不一樣於OC的初始化,Swift中並不須要返回值。閉包

類類型的實例對象還能夠定義析構器(deinitializer),它在實例對象被銷燬以前執行一些自定義的清理工做。app

類和結構體必須在初始化時給全部的存儲式屬性設定合適的初始化值,在實例對象建立後,存儲式屬性不容許處於不肯定的狀態。能夠經過初始化給存儲式屬性設定初始值,也能夠直接在定義它們的時候就給定默認值。(初始化中設定屬性的值會直接改變它的值,而不會觸發任何該屬性的觀察者)ide

 

初始化器函數

當建立某個類型的實例對象時,會自動調用初始化器,它的最簡形式,就像是一個用init關鍵字的沒有任何參數的實例方法:ui

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

 這裏定義了一個沒有參數的初始化方法init,它沒類實例對象建立的時候,將存儲屬性的值設定爲一個定值。this

如前所述,存儲屬性的初始值能夠在初始化方法中設定,也能夠做爲定義的一部分直接給出,特別地,若是每次初始化的時候都是給屬性賦的同一個值,那麼最好將這種初始化方式改成直接在屬性定義的時候就給出默認值,這樣作的好處是,在定義的時候就給出默認值更爲簡潔明瞭,語義上更爲連貫,而且有時候Swift能夠直接根據默認值推斷出屬性的數據類型,默認值還有助於更好地利用默認初始化器以及初始化器繼承,稍後會介紹。spa

所以上面的例子能夠簡寫爲:代理

struct Fahrenheit {
    var temperature = 32.0
}

 

自定義初始化

你能夠自定義初始化過程,好比設定輸入參數、設定可選屬性類型、給常量屬性賦值等。

初始化參數

在定義初始化器的時候你就能夠給出初始化參數來肯定自定義初始化過程當中的值的類型和名稱。初始化參數的語法和做用與函數/方法的參數徹底同樣,好比:

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)
// boilingPointOfWater.temperatureInCelsius is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius is 0.0

請注意例子中自定義初始化方法的參數名,這裏定義了兩個不一樣的自定義初始化方法,它們的參數都指定了外部和內部名稱。

初始化方法和普通的函數或者方法有一點區別,就是在調用的時候並無寫它的名稱,或者說它們的名字都是init,所以當自定義多個初始化方法的時候,參數名稱就變得尤其重要,這是在調用的時候用來肯定究竟選用哪一個初始化方法的依據,所以,上邊的例子中兩個初始化方法給參數指定了不一樣的外部參數名稱,在調用的時候就能知道到底是調用哪一個初始化方法了,特別地,當你沒有給參數指定外部名稱的時候,Swift會自動給初始化方法的參數指定外部名稱,它和其內部名稱同樣,就好像你在每一個參數前加上了(#)符號。所以,若是初始化方法指定了參數外部名稱(自動或手動),在調用初始化方法的時候,參數的外部名稱是必需要寫明的,省略外部名稱會觸發編譯錯誤。

若是你不想給某個初始化方法的參數指定外部名稱,那麼用下劃線來代替:

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 bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius is 37.0

 

可選屬性類型

若是你但願有一個存儲屬性能夠是「沒有值」的--好比可能在初始化時無法肯定它的值,或者是它在之後的某個時候多是「沒有值」的,那麼你能夠把這個屬性聲明爲可選類型(optional type)。

可選類型的屬性默認會被賦值爲nil,標明它如今是「沒有值」的,好比:

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()
// prints "Do you like cheese?"
cheeseQuestion.response = "Yes, I do like cheese.」

這裏,只有當問題提出以後,才能知道如何回答,因此response被設定爲了可選String。

 

給常量屬性特定值

在初始化過程當中的任什麼時候候,你均可以設定或者改變常量屬性的值,只要初始化過程結束,你就不能再改變常量屬性的值了。

對類對象而言,常量屬性只能在定義類的初始化過程當中改變,即便在子類中也不能改變。

 

默認初始化器

對於那些給全部屬性設定了默認值而且一個初始化方法都沒有提供的結構或者基類,Swift都自動提供了一個默認初始化方法,這個方法只是簡單地建立了一個新實例對象,而後把全部的屬性設定爲他們的默認值。好比:

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

 

結構體類型的逐成員初始化

若是沒有定義任何初始化方法,那麼結構體類型自動得到一個初始化方法,不一樣於上面介紹的默認初始化方法,結構體的這個初始化方法是逐成員的,即便有的屬性並無默認值。這種初始化方法是初始化新實例對象全部屬性的簡寫形式,好比:

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

 

值類型的初始化代理

初始化方法能夠調用其餘的初始化方法來做爲本身初始化的一部分,這叫作初始化代理,它提升了代碼的複用性,避免了不少初始化代碼的重複。

至於初始化代理如何工做以及被容許以何種形式,對於值類型和類類型而言是不一樣的。

值類型(結構體和枚舉)是不能繼承的,所以它們的初始化代理很是簡單,由於它們只可以代理給另外一個它們本身提供的初始化方法。然而類能夠從其餘類繼承,這意味着類在初始化的時候還必須確保它們繼承來的存儲屬性也都可以被賦予合適的值。

對於值類型,在自定義初始化方法時能夠用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 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) //注意這裏
    }
}

這裏Rect的第一個初始化方法init()其實和若是沒有提供任何初始化方法時系統給出的默認初始化方法是同樣的,它就是簡單生成一個新的實例對象,屬性值都設爲默認值。第二個初始化方法和逐參數初始化方法同樣,第三個初始化方法是自定義的,它調用了已有的初始化方法做爲其初始化的一部分。

 

類繼承與初始化

全部類的屬性--包括他從超類繼承來的全部屬性,都必須在初始化的時候賦予合適的值。

Swift給類定義了兩種初始化方法來確保全部的屬性都被賦予了合適的值,指派初始化(Designated Initializer)和便捷初始化(Convenience Initializer)。

指派初始化是類的主要初始化方式,它把類自己定義的屬性所有初始化,而後調用超類的初始化方法來依次完成繼承鏈上超類的初始化。類通常沒有不少指派初始化方法,最一般的狀況是隻擁有一個。每一個類必須擁有一個指派初始化方法,有些狀況下,經過繼承超類的指派初始化方法就能夠知足這個要求。

便捷初始化是類的次要的一種初始化方式,經過將某些參數設定默認值的方式調用同一個類的指派初始化方法,就能夠定義一個便捷初始化方法,你也能夠爲了建立一個有特殊用途的實例對象而定義便捷初始化方法。便捷初始化方法不是必需的。

指派初始化方法的語法和值類型的簡單初始化語法同樣:init(){}

便捷初始化方法的語法只須要在前面加上convenience關鍵字便可:convenience init(){}

爲了簡化指派初始化方法和便捷初始化方法之間的關係,Swift給初始化方法之間的代理制定瞭如下三條規則:

》指派初始化方法必須調用它的直接超類的指派初始化方法

》便捷初始化方法必須調用同一個類的其餘初始化方法

》便捷初始化方法必須最終調用一個指派初始化方法

這三條規則能夠簡化爲:指派初始化方法必須向上代理,便捷初始化方法必須橫向代理。

 

Swift中類的初始化是一個分兩階段的過程,第一個階段每一個存儲屬性被聲明它的類賦予初始值,當全部存儲屬性的值都被設定初始狀態以後,第二階段開始了,每一個類均可以在實例對象被建立完成以前進一步自定義它的存儲屬性的值。

這個兩步走的初始化過程既保證了初始化的安全,又給予繼承鏈上全部類足夠的靈活性。它阻止了在屬性初始化以前對它們的訪問,也阻止了從其餘初始化方法中錯誤地將屬性值改變了。

Swift編譯器在兩步走初始化過程當中執行了四次安全檢查以確保初始化正確完成:

》指派初始化方法必須在向上(超類的指派初始化方法)代理以前確保全部該類聲明的屬性被正確初始化

  如前所述,一個對象的內存分配只有在全部屬性的初始狀態都肯定了以後才能完成,所以,指派初始化方法必須確保這條規則

》指派初始化方法必須在給繼承屬性賦值以前向上代理。

  若是不這樣,初始化方法賦給繼承屬性的新值會被超類的初始化方法覆蓋掉

》便捷初始化方法必須在給任何屬性(包括當前類聲明的屬性)賦值以前橫向代理

  若是不這樣,它給屬性賦的新值會被指派初始化方法覆蓋掉(便捷初始化必須最終調用指派初始化方法)

》任何初始化方法在初始化的第一個步驟完成以前都不容許調用實例方法、訪問實例的任何屬性值或者引用self

  在第一階段完成以前,類實例並非徹底合法的。

基於上面四次安全檢查,兩步走初始化階段是按以下流程完成的:

------------------------

【第一階段】

》類的一個指派或者便捷初始化方法被調用

》類的新實例對象的內存被分配,但該內存並無被初始化

》一個指派初始化方法確保該類全部的存儲屬性都被正確的初始化了,這些存儲屬性的內存如今被初始化了

》指派初始化方法將控制權轉交給超類的指派初始化方法,來對超類的存數屬性執行相似處理

》這個過程一直在繼承鏈上持續到基類爲止

》一旦到達繼承鏈頂端的類,這個基類能夠確保全部的存儲屬性都有正確的初始化值了,實例對象被分配的內存被認爲是徹底初始化了,第一階段結束

【第二階段】

》從繼承鏈頂端開始往回,鏈上的每個指派初始化方法都有機會對實例對象進一步自定義,初始化方法如今能夠訪問self屬性,改變屬性值,調用類型方法等等

》最後,繼承鏈中的任何便捷初始化方法都有機會經過self對實例對象進行進一步自定義

------------------------

 

初始化方法的繼承和重載

和OC不同,Swift中的子類並不會默認從超類中繼承初始化方法。

若是你但願在子類中使用和超類同樣的一個或多個初始化方法,你能夠在子類中自定義初始化方法將這些初始化方法集中實現。

若是你在子類中自定義的指派初始化方法和超類中的指派初始化方法是匹配的,那麼你就是重載了這個初始化方法,所以,你必須在子類的初始化方法以前寫上override關鍵字,即便你是重載的系統提供的默認初始化方法。

就像重載屬性、方法和下標同樣,override關鍵字會促使Swift檢查超類是否有相匹配的指派初始化方法以及重載時參數是否正確。

在重載超類指派初始化方法時,你必須寫override關鍵字,即便你是用便捷初始化方法重載的也同樣。相反,若是你在子類中定義的初始化方法和超類中便捷初始化方法匹配,你不須要寫override關鍵字,由於根據前面的規則,你不能在子類中直接調用父類的便捷初始化方法,所以嚴格來講,子類並非提供了父類初始化方法的重載版本。

好比,定義一個基類,他有一個存儲屬性,在定義的時候給定了默認值,一個只讀計算屬性:

class Vehicle {
    var numberOfWheels = 0
    var description: String {
        return "\(numberOfWheels) wheel(s)"
    }
}

這個基類給全部存儲屬性提供了默認值,而且沒有提供自定義初始化方法,所以自動得到了默認初始化方法,這個方法就是簡單地建立一個Vehicle實例對象,將其屬性設置爲默認值:

let vehicle = Vehicle()
println("Vehicle: \(vehicle.description)")
// Vehicle: 0 wheel(s)

再定義一個類繼承它,並重載初始化方法(儘管該初始化方法是系統給定的):

class Bicycle: Vehicle {
    override init() {
        super.init()
        numberOfWheels = 2
    }
}

注意,在子類的指派初始化方法中,是在給繼承屬性賦值以前向上代理的,這遵循了前面提到的規則。

如前所述,默認狀況下,子類並不會繼承超類的初始化方法,可是,當某些條件知足的時候,超類的初始化方法會自動地被子類繼承,在實際開發中,這意味着你在不少狀況下都不用重載超類的初始化方法,只要是安全的,就能夠直接從超類繼承初始化方法。

假設你爲子類的全部新屬性都提供了默認值,以下兩個規則會自動生效:

》若是你的子類沒有提供任何指派初始化方法,它會自動繼承全部它超類的指派初始化方法

》若是你的子類提供了全部它超類的指派初始化方法的實現--要麼經過上面規則1繼承,或者本身提供另外的實現--那麼它自動繼承全部它超類的便捷初始化方法。

即便你的子類提供了另外的便捷初始化方法,這些規則也會生效。即便你是將超類的指派初始化方法在子類中實現爲便捷初始化方法,這一樣知足規則2的條件。

 

實際運用指派初始化和便捷初始化

下面的示例定義了具有繼承關係的三個類:Food, RecipeIngredient, 和 ShoppingListItem,並演示了它們的初始化方法是如何互相做用的。

基類是Food,有一個存儲屬性和兩個初始化方法:

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

類不像結構體,不會自動得到逐成員的初始化方法,所以它本身提供了接受一個參數的初始化方法,能夠給惟一的存儲屬性設定值。所以,第一個初始化方法就是指派初始化方法,由於它確保了類的新實例對象的全部存儲屬性都被正確初始化了。Food是基類,所以它的指派初始化不用向上代理。它的第二個初始化方法就是便捷初始化方法了,它調用了本類中的其餘初始化方法而且最終調用了指派初始化方法,即橫向代理,它不接受參數,而給存儲屬性設定一個固定值。

RecipeIngredient繼承自Food類,它定義了本身的新存儲屬性quantity,固然,它也集成了Food的存儲屬性name,它還定義了兩個初始化方法:

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自定義的第一個初始化方法明顯是指派初始化方法,它確保了本身全部存儲屬性都被正確初始化,而且是向上代理的。同時,它還自定義了一個便捷初始化方法,這個方法雖然自己是便捷初始化方法,可是它與超類的指派初始化方法是匹配的,應該認爲是將超類的指派初始化方法重載爲了便捷初始化方法,所以必須加上override關鍵字。

根據上面的規則二,若是子類提供了全部它超類的指派初始化方法的實現(即便是用便捷初始化方法實現),它自動繼承了超類的全部便捷初始化方法。

在這個例子中,RecipeIngredient的超類是Food,Food只有一個便捷初始化方法init(),根據規則二,它被RecipeIngredient繼承了,RecipeIngredient中被繼承的init()實際工做狀況與它在Food類中是同樣的,惟一一點不一樣的是它在橫向代理的時候不是調用的Food的指派初始化方法,而是RecipeIngredient中的對應指派初始化方法了。

能夠有以下三種方式來建立子類的實例對象:

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

繼承鏈上的第三級是繼承自RecipeIngredient的ShoppingListItem類,它定義了一個本身的新存儲屬性以及一個只讀計算屬性:

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

ShoppingListItem並無提供初始化方法來初始化存儲屬性purchased,由於做爲定義的一部分,它已經被初始化爲一個定值了。

ShoppingListItem爲它本身的全部存儲屬性提供了初始值,而且它本身沒有提供任何初始化方法,根據上面的規則一,它自動繼承了它超類的全部指派和便捷初始化方法。

你能夠用它繼承的三個初始化方法建立新的實例對象:

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 ✘

 

容許失敗的初始化

有時候會須要定義一個初始化方法容許失敗的類、結構或枚舉。這個失敗多是不少緣由致使的,好比非法的初始化參數值、缺乏必要的外部資源等等。

爲了應對這種可能致使初始化失敗的狀況,能夠定義一個或多個容許失敗的初始化方法。它的語法是在init關鍵字後面加上一個問號。

注意:你不能同時定義兩個擁有相同參數名稱和類型的一個容許失敗一個不容許失敗的初始化方法。

容許失敗的初始化方法返回一個可選類型(optional type),在容許失敗的初始化內,若是條件觸發了初始化失敗,那麼你就在那裏返回nil。

注意:嚴格來講,初始化方法並不會返回任何值,它只是確保當初始化結束的時候self已經被徹底和正確地初始化了,因此儘管你是用return nil來觸發初始化失敗,可是當初始化成功的時候你不須要用return關鍵字。

以下的例子定義了一個Animal結構體,它定義了一個容許失敗的初始化方法:

struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}
let someCreature = Animal(species: "Giraffe")
// someCreature is of type Animal?, not Animal
 
if let giraffe = someCreature {
    println("An animal was initialized with a species of \(giraffe.species)")
}
// prints "An animal was initialized with a species of Giraffe」
let anonymousCreature = Animal(species: "")
// anonymousCreature is of type Animal?, not Animal
 
if anonymousCreature == nil {
    println("The anonymous creature could not be initialized")
}
// prints "The anonymous creature could not be initialized」

注意:這裏在容許失敗的初始化方法中,檢查species是否爲空和檢查它是否爲nil是不同的,檢查是否爲nil是在它爲可選類型(optional string type)時才用的。

 

枚舉類型的可失敗初始化方法

你能夠用可失敗的初始化方法來根據一個或多個參數來選擇合適的枚舉成員,若是參數不能匹配一個合適的枚舉成員,那麼就觸發初始化方法失敗。好比:

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

具備原始值(raw value)的枚舉會自動得到容許失敗的初始化方法:init?(rawValue:),它接受一個名爲rawValue的對應原始值類型的參數,若是發現有一個匹配項,就返回那個枚舉成員,不然就觸發初始化失敗。好比上面的例子能夠改成擁有原始值的枚舉:

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.」

 

類的可失敗初始化方法

值類型(結構體、枚舉)的可失敗初始化方法能夠在其內部的任何地方觸發失敗,好比上面的Animal例子中,在存儲屬性species尚未被初始化的時候,就能夠返回nil以觸發初始化失敗了,然而,對於類來講,只能在全部該類定義的存儲屬性都被初始化而且初始化代理都完成以後,才能觸發初始化失敗。

下邊的例子展現了在類的可失敗初始化方法中如何顯示地展開可選項值以知足這個條件:

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

這個Product類和前面定義的Animal結構體很像,Product有一個存儲屬性name不能爲空,爲了強化這個需求,Product使用了可失敗的初始化方法來確保在初始化成功時name不是爲空的。

Product是個類,而不是結構體,所以,在觸發初始化失敗以前,全部它定義的存儲屬性都必須擁有初始化值。

在這個例子中,Product的name被定義爲隱式展開的可選String類型。由於它是可選類型,意味着在被初始化賦值以前它擁有默認值nil,這也就意味着Product所定義的全部存儲屬性都具有初始值了,所以,在可失敗初始化方法中,能夠在給name賦值以前就判斷其是否爲空並觸發初始化失敗。

由於name是常量屬性,所以一旦初始化成功,你就能夠肯定它必定是一個非nil值,儘管它被定義爲隱式展開可選類型,你始終能夠獲取它隱式展開的值,而無需檢查:

if let bowTie = Product(name: "bow tie") {
    // no need to check if bowTie.name == nil
    println("The product's name is \(bowTie.name)")
}
// prints "The product's name is bow tie」

 

初始化失敗的傳遞

類、結構體或枚舉的可失敗初始化均可以代理給這個類、結構或枚舉的另外一個可失敗初始化方法。相似地,子類的可失敗初始化能夠代理超類的可失敗初始化方法。無論哪一種狀況,若是你代理的另外一個可失敗初始化方法觸發了失敗,那麼整個初始化直接失敗,不會再執行任何其餘剩下的初始化代碼。

下面的例子定義了一個繼承自Product的子類CarItem,它定義了一個存儲屬性quantity並確保它的值不小於1:

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

這裏的quantity屬性被定義爲隱式展開可選類型,所以在被賦值以前它具有默認值nil,CarItem在給本身定義的存儲屬性賦值以後,向上代理了超類的初始化方法,這知足了類的可失敗初始化方法規則。若是超類觸發了初始化失敗,CarItem的初始化會當即觸發失敗,不會再執行剩下的代碼,若是超類初始化成功,CarItem的初始化方法確保它的quantity不小於1:

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

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

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

 

重載可失敗初始化方法

在子類中能夠重載超類中的可失敗初始化方法,而且,你能夠在子類中用不可失敗初始化方法來重載超類中的可失敗初始化方法。這讓你能夠定義一個初始化不容許失敗的類,即便她的超類的初始化方法是可失敗的。反之則不行,不能用不可失敗初始化方法代理超類中可失敗初始化方法。

若是你在子類中用不可失敗初始化方法重載超類中的可失敗初始化方法,子類的初始化方法就不能向上代理超類的初始化方法,一個不可失敗初始化方法永遠不能代理一個可失敗初始化方法。

一下例子定義了一個類Document,它能夠用一個要麼非空字符串要麼nil的name屬性來初始化:

class Document {
    var name: String?
    // this initializer creates a document with a nil name value
    init() {}
    // this initializer creates a document with a non-empty name value
    init?(name: String) {
        self.name = name
        if name.isEmpty { return nil }
    }
}

接下來定義一個繼承自Document的子類AutomaticallyNamedDocument,它把Document定義的兩個指派初始化方法都重載了:

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並無可失敗初始化方法,相反,它老是建立一個新實例對象,並確保name不會爲空。

 

「init!」式可失敗初始化

你能夠經過在【init?】的方式定義可失敗的初始化方法,它建立一個對應類型的可選(optional)類型,同時,你還能夠經過【init!】來定義可失敗的初始化方法,它建立一個對應類型的隱式展開的可選項類型。

你能夠從init?代理init!,反之亦然,你能夠用init?重載init!,反之亦然,你能夠從init代理init!,儘管這樣作在init!致使初始化失敗時會觸發斷言。

 

必須的初始化(Required Initializers)

在類的初始化方法前面加上required關鍵字來標明該類的每一個子類都必須實現這個初始化方法,而且在子類的這個方法前面也必須加上required關鍵字來標明在繼承鏈上更遠的子類也必須實現這個初始化方法,當重載一個required初始化方法時,你不須要在前面加上override關鍵字。若是你可以經過繼承來的初始化方法知足條件,你不須要顯示地實現required的初始化方法。

 

用閉包或者函數給屬性設定默認值

若是一個存儲屬性須要一些自定義或者其餘處理,你能夠用一個閉包或者全局函數來爲該屬性提供自定義的默認值。當擁有那個屬性的實例對象被初始化,這個閉包或者函數就被調用了,它的返回值就被做爲默認值賦給了屬性。這些閉包或函數其實首先建立了一個該屬性類型的臨時值,而後將該值定製爲須要的初始狀態,最後將這個臨時值返回,做爲初始值賦給屬性。大概形式以下:

class SomeClass {
    let someProperty: SomeType = {
        // create a default value for someProperty inside this closure
        // someValue must be of the same type as SomeType
        return someValue
        }()
}

這裏閉包的後面緊跟了一組空括號,它通知Swift當即執行這個閉包。若是沒有這對括號,那麼是把閉包自己賦值給了屬性,而非它的返回值。

注意:當你用閉包初始化一個屬性時,記住在閉包執行時,實例對象尚未被初始化,所以在閉包中不能訪問任何其餘屬性,即便它們有默認值也不行,你也不能訪問隱式屬性self或者調用實例方法。

struct Checkerboard {
    let boardColors: [Bool] = {
        var temporaryBoard = [Bool]()
        var isBlack = false
        for i in 1...10 {
            for j in 1...10 {
                temporaryBoard.append(isBlack)
                isBlack = !isBlack
            }
            isBlack = !isBlack
        }
        return temporaryBoard
    }()
}

這裏定義的結構體有一個常量存儲屬性boardColors,它是一個Bool型數組,在初始化的時候,它被初始化爲包含100個元素,他們是交替存儲的true和false。

相關文章
相關標籤/搜索