OneDayOneSwift[14] - Initialization

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

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

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

默認(rèn)屬性值

ps: 如果一個(gè)屬性總是使用相同的初始值场刑,那么為其設(shè)置一個(gè)默認(rèn)值比每次都在構(gòu)造器中賦值要好般此。兩種方法的效果是一樣的,只不過使用默認(rèn)值讓屬性的初始化和聲明結(jié)合得更緊密牵现。使用默認(rèn)值能讓你的構(gòu)造器更簡潔铐懊、更清晰,且能通過默認(rèn)值自動(dòng)推導(dǎo)出屬性的類型瞎疼;同時(shí)科乎,它也能讓你充分利用默認(rèn)構(gòu)造器、構(gòu)造器繼承等特性

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

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

可選屬性類型

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

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

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

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

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

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

如果你為某個(gè)值類型定義了一個(gè)自定義的構(gòu)造器,你將無法訪問到默認(rèn)構(gòu)造器(如果是結(jié)構(gòu)體耻台,還將無法訪問逐一成員構(gòu)造器)空免。這個(gè)限制可以防止你為值類型定義了一個(gè)進(jìn)行額外必要設(shè)置的復(fù)雜構(gòu)造器之后,別人還是錯(cuò)誤地使用了一個(gè)自動(dòng)生成的構(gòu)造器盆耽。

ps: 假如你希望默認(rèn)構(gòu)造器蹋砚、逐一成員構(gòu)造器以及你自己的自定義構(gòu)造器都能用來創(chuàng)建實(shí)例扼菠,可以將自定義的構(gòu)造器寫到擴(kuò)展(extension)中,而不是寫在值類型的原始定義中坝咐。

下面例子將定義一個(gè)結(jié)構(gòu)體Rect循榆,用來代表幾何矩形。這個(gè)例子需要兩個(gè)輔助的結(jié)構(gòu)體Size和Point墨坚,它們各自為其所有的屬性提供了初始值0.0秧饮。你可以通過以下三種方式為Rect創(chuàng)建實(shí)例——使用被初始化為默認(rèn)值的origin和size屬性來初始化;提供指定的origin和size實(shí)例來初始化泽篮;提供指定的center和size來初始化盗尸。在下面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òu)造器代理規(guī)則

為了簡化指定構(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)造器必須最終導(dǎo)致一個(gè)指定構(gòu)造器被調(diào)用亏拉。

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

  • 指定構(gòu)造器必須總是向上代理
  • 便利構(gòu)造器必須總是橫向代理
initializerDelegation01_2x.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责嚷。

ps:這些規(guī)則不會(huì)影響類的實(shí)例如何創(chuàng)建。任何上圖中展示的構(gòu)造器都可以用來創(chuàng)建完全初始化的實(shí)例掂铐。這些規(guī)則只影響類定義如何實(shí)現(xiàn)罕拂。

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

initializerDelegation02_2x.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)造器意外地賦予不同的值躏尉。

ps: 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作為一個(gè)值。

類實(shí)例在第一階段結(jié)束以前并不是完全有效的碳褒。只有第一階段完成后折砸,該實(shí)例才會(huì)成為有效實(shí)例,才能訪問屬性和調(diào)用方法沙峻。

以下是兩段式構(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)造器的繼承和重寫

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

ps: 父類的構(gòu)造器僅會(huì)在安全和適當(dāng)?shù)那闆r下被繼承蚕钦。

當(dāng)你在編寫一個(gè)和父類中指定構(gòu)造器相匹配的子類構(gòu)造器時(shí)亭病,你實(shí)際上是在重寫父類的這個(gè)指定構(gòu)造器。因此嘶居,你必須在定義子類構(gòu)造器時(shí)帶上override修飾符罪帖。即使你重寫的是系統(tǒng)自動(dòng)提供的默認(rèn)構(gòu)造器,也需要帶上override修飾符

ps: 當(dāng)你重寫一個(gè)父類的指定構(gòu)造器時(shí)邮屁,你總是需要寫override修飾符胸蛛,即使你的子類將父類的指定構(gòu)造器重寫為了便利構(gòu)造器。

相反樱报,如果你編寫了一個(gè)和父類便利構(gòu)造器相匹配的子類構(gòu)造器葬项,由于子類不能直接調(diào)用父類的便利構(gòu)造器,因此,嚴(yán)格意義上來講迹蛤,你的子類并未對(duì)一個(gè)父類構(gòu)造器提供重寫民珍。最后的結(jié)果就是,你在子類中“重寫”一個(gè)父類便利構(gòu)造器時(shí)盗飒,不需要加override前綴嚷量。

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

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

假設(shè)你為子類中引入的所有新屬性都提供了默認(rèn)值,以下 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)造器傻粘。(即使屬性沒有默認(rèn)值每窖,只要實(shí)現(xiàn)了父類的所有指定構(gòu)造器,就會(huì)自動(dòng)繼承父類的所有便利構(gòu)造器)

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

ps: 對(duì)于規(guī)則 2,子類可以將父類的指定構(gòu)造器實(shí)現(xiàn)為便利構(gòu)造器稽莉。

可失敗構(gòu)造器

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

ps: 可失敗構(gòu)造器的參數(shù)名和參數(shù)類型将饺,不能與其它非可失敗構(gòu)造器的參數(shù)名贡避,及其參數(shù)類型相同。
嚴(yán)格來說予弧,構(gòu)造器都不支持返回值刮吧。因?yàn)闃?gòu)造器本身的作用,只是為了確保對(duì)象能被正確構(gòu)造掖蛤。因此你只是用return nil表明可失敗構(gòu)造器構(gòu)造失敗杀捻,而不要用關(guān)鍵字return來表明構(gòu)造成功。

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

帶原始值的枚舉類型會(huì)自帶一個(gè)可失敗構(gòu)造器init?(rawValue:)蚓庭,該可失敗構(gòu)造器有一個(gè)名為rawValue的參數(shù)致讥,其類型和枚舉類型的原始值類型一致,如果該參數(shù)的值能夠和某個(gè)枚舉成員的原始值匹配器赞,則該構(gòu)造器會(huì)構(gòu)造相應(yīng)的枚舉成員垢袱,否則構(gòu)造失敗

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

值類型(也就是結(jié)構(gòu)體或枚舉)的可失敗構(gòu)造器,可以在構(gòu)造過程中的任意時(shí)間點(diǎn)觸發(fā)構(gòu)造失敗港柜。甚至在屬性被初始化前请契。

而對(duì)類而言,可失敗構(gòu)造器只能在類引入的所有存儲(chǔ)型屬性被初始化后,以及構(gòu)造器代理調(diào)用完成后爽锥,才能觸發(fā)構(gòu)造失敗涌韩。

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

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

類,結(jié)構(gòu)體救恨,枚舉的可失敗構(gòu)造器可以橫向代理到類型中的其他可失敗構(gòu)造器贸辈。類似的释树,子類的可失敗構(gòu)造器也能向上代理到父類的可失敗構(gòu)造器肠槽。

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

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

如同其它的構(gòu)造器,你可以在子類中重寫父類的可失敗構(gòu)造器奢啥〗障桑或者你也可以用子類的非可失敗構(gòu)造器重寫一個(gè)父類的可失敗構(gòu)造器。這使你可以定義一個(gè)不會(huì)構(gòu)造失敗的子類桩盲,即使父類的構(gòu)造器允許構(gòu)造失敗寂纪。

ps: 你可以用非可失敗構(gòu)造器重寫可失敗構(gòu)造器,但反過來卻不行赌结。

當(dāng)你用子類的非可失敗構(gòu)造器重寫父類的可失敗構(gòu)造器時(shí)捞蛋,向上代理到父類的可失敗構(gòu)造器的唯一方式是對(duì)父類的可失敗構(gòu)造器的返回值進(jìn)行強(qiáng)制解包。

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

你可以在init?中代理到init!柬姚,反之亦然拟杉。你也可以用init?重寫init!,反之亦然量承。你還可以用init代理到init!搬设,不過,一旦init!構(gòu)造失敗撕捍,則會(huì)觸發(fā)一個(gè)斷言拿穴。

必要構(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)代碼
    }
}

ps: 如果子類繼承的構(gòu)造器能滿足必要構(gòu)造器的要求狮腿,則無須在子類中顯式提供必要構(gòu)造器的實(shí)現(xiàn)腿宰。

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

如果某個(gè)存儲(chǔ)型屬性的默認(rèn)值需要一些定制或設(shè)置,你可以使用閉包或全局函數(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)值拖刃。

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

ps: 如果你使用閉包來初始化屬性删壮,請記住在閉包執(zhí)行時(shí),實(shí)例的其它部分都還沒有初始化兑牡。這意味著你不能在閉包里訪問其它屬性央碟,即使這些屬性有默認(rèn)值。同樣均函,你也不能使用隱式的self屬性亿虽,或者調(diào)用任何實(shí)例方法。

例:

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]
    }
}
let board = Checkerboard()
print(board.squareIsBlackAtRow(0, column: 1))
// 打印 "true"
print(board.squareIsBlackAtRow(9, column: 9))
// 打印 "false"

要點(diǎn)總結(jié)

有兩點(diǎn)疑惑:

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

The Product class defined above is very similar to the Animal structure seen eariler.The Product class has a constant name property that must not be allowed to take an empty string value.To enforce this requirement,the Product class uses a failable initializer to ensure that the propety's value is nonempty before allowing initialization to succeed.
However,Product is a class,not a structure.This means that unlike Animal,any failable initializer for the Product class must provide an initial value for the name property before triggering an intialization failure.
In the example above,the name property of the Product class is defined as having an implicitly unwrapped optional string type(String!).Because it is of an optional type,this means that the name property has a default value of nil before it is assigned a specific value during initialization.This default value of nil in turn means that all of the properties introduced by the Product class have a valid initial value.As a result,the failable intializer for Product can trigger an initialization failure at the start of the intializer if it is passed an empty string,before assigning a specific value to the name property within the initializer.

以上的代碼片段和描述都是從書上摘錄的苞也。令我疑惑的是洛勉,根據(jù)描述來看,再init?的方法體里如迟,self.name = name應(yīng)該放到 return nil下面去收毫。意思是說optional在構(gòu)造過程的初期是默認(rèn)為nil,因此在構(gòu)造過程的開始時(shí)期就會(huì)觸發(fā)failure intializer。但實(shí)際情況是必須先給name一個(gè)初始值才能完成構(gòu)造過程殷勘,因?yàn)楦鶕?jù)安全檢查1此再,首先要對(duì)本類新引進(jìn)的存儲(chǔ)屬性初始化,然后安全檢查2玲销,才會(huì)調(diào)用父類構(gòu)造器完成初始化输拇,這樣看來,可失敗構(gòu)造器都是在完成所有構(gòu)造后才對(duì)痒玩。在xcode里也跑了一下淳附,雖然上面的代碼是可以執(zhí)行的。但從運(yùn)行效果上來看蠢古,應(yīng)該是先完成了initialization,才進(jìn)行的可失敗判斷奴曙。

Conversely,if you write a subclass initializer that matches a superclass convenience initializer,that superclass convenience initializer can never be called directly by your subclass,as per the rules described above in Initializer Delegation For Class Types.Therefore, your subclass is not (strictly speaking)providing an override of the superclass initializer.As a result,you do not write the override modifier when providing a matching implementation of a superclass convenience initializer.

對(duì)于這段描述,我個(gè)人的理解:
第一遍的理解:(后面有第二遍看到這里時(shí)的新理解)
子類的convenience initializer為什么在嚴(yán)格意義上沒有提供一個(gè)對(duì)父類convenience initializer的override草讶?因?yàn)楦割惖腸onvenience initializer并不一定會(huì)被子類繼承(只有在某種情況下洽糟,也就是子類實(shí)現(xiàn)了所有父類的designated initializer才會(huì)觸發(fā)自動(dòng)繼承),所以當(dāng)子類沒有繼承該方法堕战,而你要給該方法加一個(gè)override是不對(duì)的坤溃。在繼承了之后加override才是對(duì)的。這樣的話就是說有時(shí)候是override了嘱丢,有時(shí)候不是override了薪介。那么swift就干脆說這個(gè)地方不用寫override啦,就給大家省事了越驻,你就不用去分析什么時(shí)候該寫override汁政,什么時(shí)候不該寫override道偷。編譯器也省得去校驗(yàn)啦。故此而有了這句話记劈。

第二次看到這里的時(shí)候又有的新的理解:如前面一句所說勺鸦,superclass的convenience initializer并不是直接的被subclass掉用。仔細(xì)推敲了一下這句話目木。普通的方法繼承可能是父類和子類里的那個(gè)方法實(shí)際上是同一個(gè)方法(即换途,地址空間是同一個(gè))而自動(dòng)繼承過來的convenience initializer并不和父類是同一個(gè)地址空間,而是單獨(dú)給子類開了一個(gè)地址空間刽射,雖然函數(shù)體是一樣的军拟。這個(gè)猜想等到實(shí)際理解了編譯器的override和方法繼承原理之后再來確定。強(qiáng)烈感覺第二個(gè)解釋是對(duì)的柄冲,第一個(gè)解釋略勉強(qiáng).

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吻谋,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌失暂,老刑警劉巖绸硕,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瓢对,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門姜盈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人配阵,你說我怎么就攤上這事馏颂。” “怎么了棋傍?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵救拉,是天一觀的道長。 經(jīng)常有香客問我瘫拣,道長亿絮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任麸拄,我火速辦了婚禮派昧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拢切。我一直安慰自己蒂萎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布淮椰。 她就那樣靜靜地躺著五慈,像睡著了一般帮毁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上豺撑,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天烈疚,我揣著相機(jī)與錄音,去河邊找鬼聪轿。 笑死爷肝,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的陆错。 我是一名探鬼主播灯抛,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼音瓷!你這毒婦竟也來了对嚼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤绳慎,失蹤者是張志新(化名)和其女友劉穎纵竖,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杏愤,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡靡砌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了珊楼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片通殃。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖厕宗,靈堂內(nèi)的尸體忽然破棺而出画舌,到底是詐尸還是另有隱情,我是刑警寧澤已慢,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布曲聂,位于F島的核電站,受9級(jí)特大地震影響蛇受,放射性物質(zhì)發(fā)生泄漏句葵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一兢仰、第九天 我趴在偏房一處隱蔽的房頂上張望乍丈。 院中可真熱鬧,春花似錦把将、人聲如沸轻专。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽请垛。三九已至催训,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宗收,已是汗流浹背漫拭。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留混稽,地道東北人采驻。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像匈勋,于是被迫代替她去往敵國和親礼旅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 本章將會(huì)介紹 存儲(chǔ)屬性的初始賦值自定義構(gòu)造過程默認(rèn)構(gòu)造器值類型的構(gòu)造器代理類的繼承和構(gòu)造過程可失敗構(gòu)造器必要構(gòu)造器...
    寒橋閱讀 770評(píng)論 0 0
  • 官方文檔 初始化 Initialization是為準(zhǔn)備使用類,結(jié)構(gòu)體或者枚舉實(shí)例的一個(gè)過程饿自。這個(gè)過程涉及了在實(shí)例里...
    hrscy閱讀 1,137評(píng)論 0 1
  • 構(gòu)造過程是使用類汰翠、結(jié)構(gòu)體或枚舉類型的實(shí)例之前的準(zhǔn)備過程。在新實(shí)例可用前必須執(zhí)行這個(gè)過程璃俗,具體操作包括設(shè)置實(shí)例中每個(gè)...
    莽原奔馬668閱讀 686評(píng)論 0 3
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理奴璃,服務(wù)發(fā)現(xiàn),斷路器城豁,智...
    卡卡羅2017閱讀 134,704評(píng)論 18 139
  • Jenny萬閱讀 245評(píng)論 0 0