Swift學(xué)習(xí)筆記(七)--構(gòu)造器

構(gòu)造器(Initializer)

其實(shí)我個(gè)人并不喜歡把init和deinit叫做構(gòu)造器和析構(gòu)器(真正應(yīng)該對應(yīng)constructor和destructor), 因?yàn)闃?gòu)造和析構(gòu)做的更多的應(yīng)該是分配和回收內(nèi)存, initializer應(yīng)該只是初始化器或初始化方法, 但是聽起來很奇怪而且也很麻煩, 加上Swift并不需要我們自己來手動(dòng)分配和回收內(nèi)存, 所以還是以構(gòu)造器和析構(gòu)器來稱呼這2個(gè)概念吧.

前言

這一段與ObjC有很大的區(qū)別, 比如之前我們是先調(diào)用父類的init再寫自己的, 但是到了Swift里面, 我們卻先初始化自己, 再初始化父類, 是相反的, 具體為什么, WWDC的視頻里面有說, 總結(jié)起來有3點(diǎn):

  1. Swift規(guī)定每個(gè)存儲(chǔ)屬性在被訪問之前都必須得到初始化(可選屬性如果是變量則會(huì)自動(dòng)初始化為nil); // 1.30更新: 之前漏了 如果是變量, 因?yàn)槌A渴遣粫?huì)自動(dòng)初始化為nil的, 即使是可選屬性, 也要自己手動(dòng)初始化.
  2. Swift中初始化是兩段式的, 如果在第二階段訪問了一個(gè)方法, 如果這個(gè)方法被子類重載了, 而且方法里面訪問了一個(gè)子類的屬性, 就會(huì)出現(xiàn)和規(guī)則違背的地方;
  3. 那為什么ObjC可以呢? 原因是ObjC會(huì)為每一個(gè)屬性默認(rèn)設(shè)置為0或者nil, 但是Swift不會(huì).
    綜合上面3個(gè)原因, 導(dǎo)致出現(xiàn)了這個(gè)情況, 具體看一個(gè)例子吧
class Human {
    var age:Int
    init(){
        age = 0
        desc()  // 如果初始化一個(gè)Man 對象, 這邊實(shí)際上會(huì)調(diào)用子類的desc方法, 
                //至于為什么還需要解釋嗎?
    }
    func desc(){
        print("Human")
    }
}

  class Man : Human {
    var prop: String
    override func desc(){
        print("Man \(prop)")
    }
    override init(){
        prop = "123"
        super.init()
    }
}

這就是為什么要先初始化自身, 再初始化父類的原因, 其中還涉及到很多別的細(xì)節(jié), 例如繼承而來的屬性怎么算?還有具體里面的兩段式是什么之后再說, 我們先按官方文檔的節(jié)奏慢慢講.

為存儲(chǔ)屬性設(shè)置初始值(Setting Initial Values for Stored Properties)

如前面所述類和結(jié)構(gòu)體在創(chuàng)建實(shí)例的時(shí)候都必須給所有的存儲(chǔ)屬性設(shè)置初始值, 可以在構(gòu)造器中設(shè)置, 也可以在聲明屬性的時(shí)候就給定. 注意: 給定初始值不會(huì)觸發(fā)屬性
監(jiān)聽.

構(gòu)造器(initializers)

構(gòu)造器會(huì)在創(chuàng)建實(shí)例的時(shí)候自動(dòng)調(diào)用, 一般來說每個(gè)類都需要構(gòu)造器, 無論是自己寫的還是編譯器為你生成的.
如果你為所有的屬性都設(shè)置了默認(rèn)值, 你可以選擇不手動(dòng)寫一個(gè)構(gòu)造器. 可以用init關(guān)鍵字來聲明一個(gè)構(gòu)造器(之前說過不需要func修飾), 括號里面可以加各種參數(shù). 如:

init(){
}
init(name:String){
    self.name = name
}
構(gòu)造參數(shù)

構(gòu)造器和函數(shù)很相似, 但也意味著構(gòu)造器和一般函數(shù)不同:

  1. 函數(shù)的第一個(gè)參數(shù)如果不手動(dòng)寫標(biāo)簽是會(huì)忽略掉的, 而構(gòu)造器不會(huì)
  2. 函數(shù)可能有返回值, 但是構(gòu)造器沒有返回值(這就是另一個(gè)原因我不喜歡講initializer為構(gòu)造器的原因)
    以官方的結(jié)構(gòu)體構(gòu)造器為例子:
struct Celsius {
    var temperatureInCelsius: Double
    init(fromFahrenheit fahrenheit: Double) {
        temperatureInCelsius = (fahrenheit - 32.0) / 1.8
    }
    init(fromKelvin kelvin: Double) {
        temperatureInCelsius = kelvin - 273.15
    }
}
// 在使用構(gòu)造器的時(shí)候, 即使只有一個(gè)參數(shù)也會(huì)有標(biāo)簽, 原因當(dāng)然是構(gòu)造器名字固定, 不能在后面加WithXXX了, 
// 如果不想要可以和函數(shù)一樣, 用下劃線(_)來隱藏
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
let freezingPointOfWater = Celsius(fromKelvin: 273.15)

可以看到, Swift里面構(gòu)造器都叫init, 而不是像ObjC一樣, 還有initWithXXX什么的, 所以構(gòu)造器(函數(shù)也是)是由名字和參數(shù)類型來統(tǒng)一標(biāo)識(shí)的.

可選類型與常量

可選類型會(huì)被默認(rèn)置為nil, 所以并不強(qiáng)制在初始化時(shí)賦值, 而常量則必須要初始化, 或者聲明的時(shí)候給默認(rèn)值.

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

在下面兩種情況下Swift會(huì)自動(dòng)生成一個(gè)默認(rèn)構(gòu)造器

  1. 結(jié)構(gòu)體沒有手動(dòng)聲明任何構(gòu)造器(結(jié)構(gòu)體很有意思, 如果全部的非可選屬性都有默認(rèn)值, 會(huì)生成兩個(gè)默認(rèn)構(gòu)造器, 其根本規(guī)則還是和之前說的一樣, 使用實(shí)例之前, 其存儲(chǔ)屬性必須全部得到初始化)
  2. 類里面每一個(gè)非可選屬性都有了默認(rèn)值, 且沒有聲明任何構(gòu)造器

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

所謂構(gòu)造代理就是構(gòu)造器可以調(diào)用別的構(gòu)造器來輔助完成構(gòu)造過程, 主要還是為了代碼復(fù)用.
構(gòu)造代理對值類型和引用類型來說不太一樣, 值類型因?yàn)椴恢С掷^承, 所以只會(huì)用自己寫的構(gòu)造器來代理, 從而相對更簡單. 類則會(huì)有從父類繼承構(gòu)造器的情況要考慮, 不過還是那句話, 所有存儲(chǔ)屬性在構(gòu)造器中都完成初始化就可以.
值得一提的是, 如之前所說, 如果你自己實(shí)現(xiàn)了一個(gè)自定義的構(gòu)造器, 那么默認(rèn)的構(gòu)造器將不會(huì)被合成, 如果你還是想要默認(rèn)的構(gòu)造器, 就用extension來寫自定義構(gòu)造器即可.
直接上例子吧:

struct Size {
    var width = 0.0, height = 0.0 // 全部有默認(rèn)值, 會(huì)生成2個(gè)構(gòu)造器
}
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)造器代理
    }
}

類繼承與初始化

再次強(qiáng)調(diào), 類的所有存儲(chǔ)屬性包括從父類繼承而來的都必須在初始化的時(shí)候賦予初值.
Swift中定義了兩種類型的構(gòu)造器來確保所有的存儲(chǔ)屬性都獲得初值, 即指派構(gòu)造器和便利構(gòu)造器.

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

指派構(gòu)造器是主要的構(gòu)造手段, 每個(gè)類至少要有1個(gè), 意味著可以有多個(gè), 但是多數(shù)情況下會(huì)是1個(gè), 而便利構(gòu)造器則沒有要求.
對指派構(gòu)造器的要求則是必須真正完成所有存儲(chǔ)屬性的初始化, 而便利構(gòu)造器則要求最終要調(diào)用一個(gè)指派構(gòu)造器, 這兩者的關(guān)系比較像之前提到的構(gòu)造代理. 在一些細(xì)節(jié)上會(huì)有區(qū)分, 先慢慢來看.
從語法上, 如果僅僅是init, 就會(huì)是指派構(gòu)造器, 用convenience來修飾, 就是便利構(gòu)造器. 如:

init(parameters) {
    statements
}
convenience init(parameters) {
    statements
}
類類型的構(gòu)造代理

為了描述清楚指派構(gòu)造器和便利構(gòu)造器之間的關(guān)系, Swift有以下3個(gè)法則:
法則1:
一個(gè)指派構(gòu)造器必須調(diào)用它的直接父類的指派構(gòu)造器(至于為什么, 可以下面這個(gè)例子看看):

class Root {
    var a : Int
    init(){
        a = 0
    }
    convenience init(i:Int){
        self.init()
        a = i
        print(self)  // 打印兩次, 第一次是Root, 第二次是Node
    }
}

class Node : Root {
    var b: Int
    override init(){
        b = 1
        super.init()
    }
}

var root = Root(i: 2)
var node = Node(i: 2)

法則2:
便利構(gòu)造器必須調(diào)用類中其它的構(gòu)造器

法則3:
便利構(gòu)造器必須最終調(diào)用一個(gè)指派構(gòu)造器(這個(gè)是必須的, 畢竟至于指派構(gòu)造器才要求初始化所有存儲(chǔ)屬性)

Tip: 其實(shí)一開始很奇怪為什么一定要有個(gè)便利構(gòu)造器的概念, 其實(shí)是因?yàn)镾wift對初始化的檢查非常的嚴(yán)格(稍候還會(huì)講繼承和二段式), 所以便利構(gòu)造器就是為了不要反復(fù)進(jìn)行檢查, 一般普遍的做法是給傳一些默認(rèn)參數(shù), 例如UIView中, 假如init(frame:CGRect)是指派構(gòu)造器, 那么init()就可以定義為便利構(gòu)造器, 直接調(diào)用self.init(frame:CGRectZero)即可

兩段式初始化

Swift中類的初始化分為兩段式:

  1. 第一階段, 每個(gè)存儲(chǔ)屬性都賦予初值
  2. 對實(shí)例進(jìn)行進(jìn)一步操作(例如加個(gè)監(jiān)聽什么的)
    之所以有兩段式其實(shí)還是為了確保開發(fā)者不會(huì)在屬性未得到初始化就訪問屬性的情況.
    所以, 可以預(yù)見的是, 在一個(gè)稍微復(fù)雜的類中, 至少會(huì)天然出現(xiàn)2段截然不同的代碼, 一段是不停地賦值, 一段是做一些其它的操作.
    同時(shí), Swift的編譯器會(huì)為兩段式初始化做一系列的檢查, 以確保初始化完成(這段比較重要, 所以直接全部翻譯了):
    安全檢查1:
    把構(gòu)造過程交給父類前, 指派構(gòu)造器必須保證聲明的存儲(chǔ)屬性都已初始化了.
    如上所述, 一旦所有的存儲(chǔ)屬性的初始狀態(tài)都已知之后, 這個(gè)對象的內(nèi)存才被當(dāng)做已初始化. 為了滿足這個(gè)規(guī)則, 一個(gè)指派構(gòu)造函數(shù)在移交給初始化鏈之前, 必須確定自己本類所有的屬性都已經(jīng)初始化

安全檢查2:
一個(gè)指派構(gòu)造器必須把繼承屬性賦值之后才能移交給父類初始化函數(shù). 如果不這么做, 指派構(gòu)造器的賦的新值就會(huì)被父類的初始化函數(shù)給覆蓋

安全檢查3
一個(gè)便利初始化函數(shù)在給任何屬性賦值之前(包括自己類的屬性), 必須調(diào)用其它的初始化函數(shù). 如果不這么做, 便利初始化函數(shù)賦的值將會(huì)被指派初始化函數(shù)覆蓋

安全檢查4
初始化函數(shù)不能調(diào)用其它實(shí)例方法, 不能從實(shí)例屬性中取值, 也不能用self, 直到第一階段初始化完成(其實(shí)這里Swift做的不是特別嚴(yán)謹(jǐn), 如果一個(gè)類沒有父類, 那么即使還沒有初始化完成也可以用self, 這里只能勉強(qiáng)解釋為第一二階段合二為一了.)

我們再來看看這兩段初始化到底做了些什么事情:
階段1:

  • 類的一個(gè)指派或便利初始化函數(shù)被調(diào)用
  • 類實(shí)例的內(nèi)存被分配出來, 但是這塊內(nèi)存并沒有被初始化
  • 一個(gè)類的指派初始化函數(shù)保證類所有的存儲(chǔ)屬性都有值. 到這一階段, 存儲(chǔ)屬性的內(nèi)存就已經(jīng)初始化了
  • 這個(gè)指派構(gòu)造器把初始化進(jìn)程移交給父類初始化函數(shù)來對父類存儲(chǔ)屬性實(shí)現(xiàn)同樣的操作
  • 這個(gè)過程一直沿著繼承鏈持續(xù)下去, 直到達(dá)到繼承鏈頂端
  • 到了繼承鏈頂端, 并且最終父類保證所有的存儲(chǔ)屬性都有值之后, 實(shí)例的內(nèi)存就被當(dāng)做完全初始化了, 此時(shí)階段1完成

階段2:

  • 從繼承鏈頂端倒回來(因?yàn)楹瘮?shù)調(diào)用棧啊), 每一個(gè)指派初始化函數(shù)都可以進(jìn)一步定制實(shí)例, 初始化函數(shù)至此可以訪問self, 并且可以修改自己的屬性, 調(diào)用實(shí)例方法, 等等
  • 最終, 調(diào)用鏈上的任意便利初始化方法都可以操作實(shí)例了, 也可以訪問self.

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

Swift不會(huì)像ObjC一樣, 自動(dòng)從父類那里繼承父類的構(gòu)造器, 因?yàn)镾wift不像ObjC會(huì)給屬性默認(rèn)值, 如果繼承下來的話, 可能會(huì)有屬性沒有被初始化. 當(dāng)然只是說不會(huì)自動(dòng)而已, 我們還是可以做一些事情來實(shí)現(xiàn)繼承在下一節(jié)會(huì)細(xì)講.
此外, 如果子類的指派構(gòu)造器和父類相同, 也要用override來修飾. 但是, 如果子類的指派構(gòu)造器與父類的便利構(gòu)造器相同, 那么父類的便利構(gòu)造器永遠(yuǎn)都不會(huì)被子類調(diào)用到(具體原因參考構(gòu)造代理法則1), 所以這種情況是不需要寫override的.
注意: 初始化時(shí), 子類只可以修改繼承的變量屬性, 而不能修改繼承的常量

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

如上一節(jié)稍微提過的, 雖然默認(rèn)不能繼承, 但是只要滿足下面的2種情況就可以:

規(guī)則1:
子類沒有定義任何的指派構(gòu)造器, 那么就會(huì)自動(dòng)從父類那里繼承所有的指派構(gòu)造器(例如, 所有的子類屬性都有默認(rèn)值)

規(guī)則2:
子類提供了全部的父類指派構(gòu)造器而不是從情況1獲得的, 即使是提供了一部分實(shí)現(xiàn), 那么它將自動(dòng)繼承所有的父類便利構(gòu)造器. (個(gè)人認(rèn)為, 調(diào)用實(shí)現(xiàn)的那一個(gè)指派構(gòu)造器的便利構(gòu)造器都可以, 其余的不行可以更智能, 而且也不會(huì)出問題)

注意: 子類可以實(shí)現(xiàn)父類的指派構(gòu)造器為自己的便利構(gòu)造器, 從而滿足規(guī)則2來獲得父類的構(gòu)造器. 如:

class Root {
    var a : Int
    var b: Int
    init(){
        self.a = 0
        self.b = 0
    }
// 2. 解開這段注釋下面的node聲明不滿足規(guī)則1
//    init(str:String){   
//        self.a = Int(str)!
//        self.b = Int(str)!
//    }
    convenience init(i:Int){
        self.init()
        a = i
        b = i
    }
}

class Node : Root {
    var bb: Int
    override init() {
        bb = 0
        super.init()
    }
// 3. 解開這段注釋將滿足規(guī)則2
//    convenience override init(str:String){  
//        self.init()
//        
//    }
}

var node = Node(i: 3) // 1. 子類實(shí)現(xiàn)了父類的全部指派構(gòu)造器, 滿足規(guī)則1

指派構(gòu)造器和便利構(gòu)造器實(shí)例

文檔給出了一個(gè)例子來進(jìn)行講解, 直接先看代碼:

class Food {
    var name: String
    init(name: String) {
        self.name = name
    }
    convenience init() { // 便利構(gòu)造器用: 給出默認(rèn)參數(shù)
        self.init(name: "[Unnamed]")
    }
}
let namedMeat = Food(name: "Bacon")
let mysteryMeat = Food()

class RecipeIngredient: Food {
    var quantity: Int
    init(name: String, quantity: Int) {
        self.quantity = quantity
        super.init(name: name)
    }
    override convenience init(name: String) { // 重載父類的指派構(gòu)造器, 滿足規(guī)則2
        self.init(name: name, quantity: 1)
    }
}
let oneMysteryItem = RecipeIngredient() // 繼承而來的構(gòu)造器
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)

class ShoppingListItem: RecipeIngredient {
    var purchased = false
    var description: String {
        var output = "\(quantity) x \(name)"
        output += purchased ? " ?" : " ?"
        return output
    }
}
// 因?yàn)镾hoppingListItem滿足規(guī)則1, 所以全部繼承下來了
var breakfastList = [
    ShoppingListItem(),
    ShoppingListItem(name: "Bacon"),
    ShoppingListItem(name: "Eggs", quantity: 6),
]

構(gòu)造失敗(Failable initializer)

有時(shí)候需要返回構(gòu)造失敗, 例如傳入了不恰當(dāng)?shù)膮?shù). 這種情況主要是引起調(diào)用方的注意, 在傳入關(guān)鍵參數(shù)的時(shí)候要當(dāng)心.
可失敗的構(gòu)造器聲明也很簡單, 用init?就可以的, 但是要注意, 可失敗和不可失敗的構(gòu)造器不能有一樣的參數(shù)類型列表, 否則就有二義性了.
構(gòu)造失敗, 自然就是返回nil了, 所以可失敗的構(gòu)造器返回值是Optional的, 在使用的時(shí)候要注意拆包.
需要注意的是, 值類型和引用類型在處理失敗構(gòu)造的時(shí)候有些許不一樣, 先看值類型的:

struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }  // return nil 在任意位置都可以
        self.species = species
    }
}

enum TemperatureUnit {
    case Kelvin, Celsius, Fahrenheit
    init?(symbol: Character) {
        switch symbol {
        case "Z":
            return nil    // 寫在哪里都是可以的
        case "K":
            self = .Kelvin
        case "C":
            self = .Celsius
        case "F":
            self = .Fahrenheit
        default:
            return nil
        }
    }
}
// 枚舉還有另外一種寫法, 因?yàn)闆]有可以通過rawValue來創(chuàng)建
enum TemperatureUnit: Character {
    case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
}
TemperatureUnit(rawValue: "X")  // 這里返回nil

類的失敗構(gòu)造

上一節(jié)可以看到, 值類型的失敗構(gòu)造可以發(fā)生在初始化進(jìn)程的任何點(diǎn)上. 但是對于類而言, 只有在所有的存儲(chǔ)屬性都賦予了初值之后才能出發(fā)失敗構(gòu)造. 例如:

class Product {
    let name: String!  // 如果不記得這里的感嘆號代表什么就去看第一章
    init?(name: String) {
        self.name = name
        if name.isEmpty { return nil }
    }
}

其實(shí)個(gè)人不是很明白這里為什么要限定這么死, 但是官方文檔也沒有講更多的信息了, 只能當(dāng)做一項(xiàng)規(guī)定來看了.

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

類,結(jié)構(gòu)體和枚舉的構(gòu)造失敗是會(huì)傳遞到其它的構(gòu)造器去的, 而且子類構(gòu)造失敗還會(huì)傳遞到父類的可失敗構(gòu)造器中去. 從而整個(gè)初始化進(jìn)程都會(huì)直接失敗掉, 不會(huì)再執(zhí)行其它的代碼了.
注意: 一個(gè)可失敗的構(gòu)造器也是可以調(diào)用非可失敗構(gòu)造器的. 通過這種方式, 我們可以在現(xiàn)有的構(gòu)造進(jìn)程中添加一個(gè)潛在失敗狀態(tài).
以一個(gè)例子來講解:

class CartItem: Product {
    let quantity: Int!
    init?(name: String, quantity: Int) {
        self.quantity = quantity
        super.init(name: name)
        if quantity < 1 { return nil }
    }
}
if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
    print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
    print("Unable to initialize zero shirts")  // 這里被打印
}
// 同樣的
if let oneUnnamed = CartItem(name: "", quantity: 1) {
    print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
    print("Unable to initialize one unnamed product") // 父類構(gòu)造失敗
}

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

和上面提到的其它構(gòu)造器重載沒有別的區(qū)別, 只是我們可以用一個(gè)不可失敗的構(gòu)造器來重載可失敗的構(gòu)造器, 但是不能反過來. 需要注意的是, 如果你重載了一個(gè)父類的可失敗構(gòu)造器, 記得要拆包. 例子:

class Fail{
    var a:Int
    init?(i:Int){
        a = i
        if i < 0 {return nil}
    }
}

class Error : Fail{
    var b: Int
    override init(i:Int){
        b = i
        super.init(i:abs(i))!  // 要加感嘆號, 當(dāng)然要確保父類可以構(gòu)造成功
    }
}
init!可失敗構(gòu)造器

對于Optional type有兩種形式, 一種是?, 一種則是!. 兩者的具體區(qū)別在第一張的基礎(chǔ)篇已經(jīng)說過. 這里再強(qiáng)調(diào)一次吧. 用?的optional type, 在使用的時(shí)候需要拆包, 而用!的optional type可以直接使用, 就像ObjC里面的指針一樣. 需要注意的是, 不管哪種形式, 都是需要判空的, 否則會(huì)出現(xiàn)crash.
所以在init?和init!的區(qū)別也在于此, 這兩者在初始化的時(shí)候是可以互相代理的. 同時(shí), 也可以在init里面把初始化交給init!代理, 只是這么做的話, 如果失敗就會(huì)觸發(fā)斷言失敗.

必需構(gòu)造器(Required Initializers)

如果init用了required來修飾, 那么意味著子類必需要重載這個(gè)構(gòu)造器.
不過值得一提的是, 如果你滿足構(gòu)造器繼承的條件的話, 必需構(gòu)造器也不是一定要實(shí)現(xiàn)的, 例如:

class Fail{
    var a:Int
    required init(){
        a = 0
    }
}

class Error : Fail{
    var b: Int = 1
}
var err = Error()
err.a  // 打印出0

用閉包或者函數(shù)來設(shè)置默認(rèn)值

在設(shè)置存儲(chǔ)屬性默認(rèn)值的時(shí)候, 可以用函數(shù)或者閉包來實(shí)現(xiàn), 例如官方的兩個(gè)例子:

// 閉包
class SomeClass {
    let someProperty: SomeType = {
        // 注意: 在這里不能用self, 更不能用其它的屬性(即使它有默認(rèn)值, 因?yàn)閟elf還沒準(zhǔn)備好)或者該類的實(shí)例方法
        // 也就是說執(zhí)行這段代碼時(shí), 初始化都還沒有進(jìn)行
        return someValue
    }()   // 最后要加上(), 不然就被當(dāng)做是個(gè)閉包, 而不是這個(gè)閉包的結(jié)果了
}

// 函數(shù), 這個(gè)例子是創(chuàng)建一個(gè)國際象棋的棋盤, 黑白間隔開的
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
    }()
    func squareIsBlackAtRow(row: Int, column: Int) -> Bool {
        return boardColors[(row * 10) + column]
    }
}

至此, 初始化就結(jié)束了, 這一章總結(jié)的不好, 基本都是按照原文翻過來的, 因?yàn)榈拇_是很多規(guī)則, 沒法解釋, 就是這么規(guī)定的. 如果一定要總結(jié)的話, 就是之前反復(fù)提到的那句話, Swift要求任何實(shí)例使用之前都要先初始化完成. 所有的那些規(guī)則基本上都是圍繞著這句話進(jìn)行的.
官網(wǎng)上有一些圖表, 有興趣可以去看看官方文檔

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

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

  • 本章將會(huì)介紹 存儲(chǔ)屬性的初始賦值自定義構(gòu)造過程默認(rèn)構(gòu)造器值類型的構(gòu)造器代理類的繼承和構(gòu)造過程可失敗構(gòu)造器必要構(gòu)造器...
    寒橋閱讀 839評論 0 0
  • 構(gòu)造過程是使用類、結(jié)構(gòu)體或枚舉類型的實(shí)例之前的準(zhǔn)備過程。在新實(shí)例可用前必須執(zhí)行這個(gè)過程,具體操作包括設(shè)置實(shí)例中每個(gè)...
    莽原奔馬668閱讀 749評論 0 3
  • 構(gòu)造過程 構(gòu)造過程是使用類、結(jié)構(gòu)體或枚舉類型的實(shí)例之前的準(zhǔn)備過程。在新實(shí)例可用前必須執(zhí)行這個(gè)過程,具體操作包括設(shè)置...
    蠱毒_閱讀 782評論 0 2
  • 123.繼承 一個(gè)類可以從另外一個(gè)類繼承方法,屬性和其他特征。當(dāng)一個(gè)類繼承另外一個(gè)類時(shí), 繼承類叫子類, 被繼承的...
    無灃閱讀 1,492評論 2 4
  • 拆書訓(xùn)練營20:讓健身走上正軌的解決方案 拆頁來源:極簡主義 拆書目標(biāo):通過對拆頁和本書的學(xué)習(xí),掌握讓健身走上正軌...
    爽妹紙閱讀 300評論 0 1

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