便捷初始化器和指定初始化器

1、自定義初始化方法要先調(diào)用自己類默認(rèn)初始化方法,自己重寫默認(rèn)初始化方法要先調(diào)用父類默認(rèn)初始化方法
2谎脯、應(yīng)該要先調(diào)用父類的構(gòu)造器或者自身的默認(rèn)構(gòu)造器恬汁,以防止先給屬性賦值了然后才調(diào)用父類或者自身的默認(rèn)構(gòu)造器把以前的賦值覆蓋了
一個(gè)類的所有存儲(chǔ)屬性-包括從父類繼承而來(lái)的屬性-都必須在初始化的時(shí)候設(shè)置初始值。
Swift為class類型定義了兩種構(gòu)造器來(lái)確保它們所有的存儲(chǔ)屬性都設(shè)置了初始值许溅。這兩種方式叫做指定構(gòu)造器和便捷構(gòu)造器。

指定構(gòu)造器和便捷構(gòu)造器
指定構(gòu)造器是一個(gè)類最主要的構(gòu)造器。指定構(gòu)造器通過(guò)設(shè)置所有屬性的初值并且調(diào)用所有的父類構(gòu)造器來(lái)根據(jù)構(gòu)造鏈一次初始化所有的屬性葫笼。

類所擁有的指定構(gòu)造器很少,一般只有一個(gè)拗馒,并且是連接這父類的構(gòu)造鏈依次完成構(gòu)造的路星。

每個(gè)類至少有一個(gè)指定構(gòu)造器,在有些情況下诱桂,需要使用繼承來(lái)從父類中得到該指定構(gòu)造器洋丐,更多內(nèi)容可以查看后面的Automatic Initializer Inheritance章節(jié)。

便捷構(gòu)造器是類的第二種常用構(gòu)造器挥等。你可以調(diào)用同一個(gè)類中的指定構(gòu)造器來(lái)定義一個(gè)便捷構(gòu)造器友绝,使用指定構(gòu)造器來(lái)設(shè)置相關(guān)的參數(shù)默認(rèn)值。你還可以定義一個(gè)便捷構(gòu)造器來(lái)創(chuàng)建這個(gè)類的實(shí)例或者是別的特殊用途肝劲。

如果你的類不需要它們迁客,也可以不定義便捷構(gòu)造器。不過(guò)對(duì)于常見(jiàn)初始化模型需要快捷方式的時(shí)候創(chuàng)建一個(gè)便捷構(gòu)造器可以讓你的初始化過(guò)程變成十分簡(jiǎn)單便捷辞槐。
構(gòu)造鏈
為了簡(jiǎn)化指定構(gòu)造器和便捷構(gòu)造器的關(guān)系掷漱,Swift為兩種構(gòu)造器的代理調(diào)用設(shè)置了三個(gè)規(guī)則:
規(guī)則1
指定構(gòu)造器必須調(diào)用它直接父類的指定構(gòu)造器
規(guī)則2
便捷構(gòu)造器只能調(diào)用同一個(gè)類中的其它構(gòu)造器
規(guī)則3
便捷構(gòu)造器必須以調(diào)用一個(gè)指定構(gòu)造器結(jié)束
記下這些規(guī)則的簡(jiǎn)單方法是:
指定構(gòu)造器必須向上代理
便捷構(gòu)造器必須橫向代理
可以使用下面的圖來(lái)表示:


父類中的兩個(gè)便捷構(gòu)造器依次調(diào)用直到指定構(gòu)造器,子類中的指定構(gòu)造器調(diào)用了父類的指定構(gòu)造器榄檬。
注意:這些規(guī)則不會(huì)影響每個(gè)類的實(shí)例創(chuàng)建過(guò)程卜范。每個(gè)構(gòu)造器都可以用來(lái)創(chuàng)建它們各自的類的實(shí)例。這些規(guī)則只影響你如何編寫類實(shí)現(xiàn)代碼鹿榜。
下圖演示的是另一種更為復(fù)雜的具有四個(gè)等級(jí)的類海雪。這個(gè)圖展示了指定構(gòu)造器在類的初始化過(guò)程中如何被作為“漏斗”節(jié)點(diǎn)的。這個(gè)構(gòu)造鏈簡(jiǎn)化了類與類之間的交互關(guān)系:
image

兩階段的初始化
在Swift中犬缨,類的初始化要經(jīng)過(guò)兩個(gè)階段喳魏。在第一個(gè)階段,每一個(gè)存儲(chǔ)屬性都被設(shè)置了一個(gè)初始值怀薛。一旦每個(gè)存儲(chǔ)屬性的值在初始化階段被設(shè)置了刺彩,在第二個(gè)階段,每個(gè)類在這個(gè)實(shí)例被使用之前都會(huì)有機(jī)會(huì)來(lái)設(shè)置它們相應(yīng)的存儲(chǔ)屬性。
兩階段的模式使初始化過(guò)程更加安全创倔,還可以讓每個(gè)類在類的層級(jí)關(guān)系中具有更多的可能性嗡害。兩階段初始化方法可以防止屬性在被初始化之前就被使用,或者是被另一個(gè)構(gòu)造器錯(cuò)誤地賦值畦攘。
注意:Swift的這種兩階段初始化方法跟Objective-C中的類似霸妹。主要的差別是在第一個(gè)過(guò)程中,Objective-C為每個(gè)屬性賦值0或者null知押,而在Swift中叹螟,可以個(gè)性化設(shè)置這些初始值,還可以處理一些初始值不能是0或者nil的情況台盯。
Swift編譯器通過(guò)四重檢查來(lái)確保兩階段式的初始化過(guò)程是完全正確無(wú)誤的:
Safety check 1
A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.

As mentioned above, the memory for an object is only considered fully initialized once the initial state of all of its stored properties is known. In order for this rule to be satisfied, a designated initializer must make sure that all its own properties are initialized before it hands off up the chain.
Safety check 2
A designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property. If it doesn’t, the new value the designated initializer assigns will be overwritten by the superclass as part of its own initialization.
Safety check 3
A convenience initializer must delegate to another initializer before assigning a value to any property (including properties defined by the same class). If it doesn’t, the new value the convenience initializer assigns will be overwritten by its own class’s designated initializer.
Safety check 4
An initializer cannot call any instance methods, read the values of any instance properties, or refer toself
as a value until after the first phase of initialization is complete.

The class instance is not fully valid until the first phase ends. Properties can only be accessed, and methods can only be called, once the class instance is known to be valid at the end of the first phase.
Here’s how two-phase initialization plays out, based on the four safety checks above:
Phase 1
A designated or convenience initializer is called on a class.
Memory for a new instance of that class is allocated. The memory is not yet initialized.
A designated initializer for that class confirms that all stored properties introduced by that class have a value. The memory for these stored properties is now initialized.
The designated initializer hands off to a superclass initializer to perform the same task for its own stored properties.
This continues up the class inheritance chain until the top of the chain is reached.
Once the top of the chain is reached, and the final class in the chain has ensured that all of its stored properties have a value, the instance’s memory is considered to be fully initialized, and phase 1 is complete.

Phase 2
Working back down from the top of the chain, each designated initializer in the chain has the option to customize the instance further. Initializers are now able to access self
and can modify its properties, call its instance methods, and so on.
Finally, any convenience initializers in the chain have the option to customize the instance and to work with self
.

Here’s how phase 1 looks for an initialization call for a hypothetical subclass and superclass:


In this example, initialization begins with a call to a convenience initializer on the subclass. This convenience initializer cannot yet modify any properties. It delegates across to a designated initializer from the same class.
The designated initializer makes sure that all of the subclass’s properties have a value, as per safety check 1. It then calls a designated initializer on its superclass to continue the initialization up the chain.
The superclass’s designated initializer makes sure that all of the superclass properties have a value. There are no further superclasses to initialize, and so no further delegation is needed.
As soon as all properties of the superclass have an initial value, its memory is considered fully initialized, and Phase 1 is complete.
Here’s how phase 2 looks for the same initialization call:
image

The superclass’s designated initializer now has an opportunity to customize the instance further (although it does not have to).
Once the superclass’s designated initializer is finished, the subclass’s designated initializer can perform additional customization (although again, it does not have to).
Finally, once the subclass’s designated initializer is finished, the convenience initializer that was originally called can perform additional customization.
構(gòu)造器的繼承和重寫
Unlike subclasses in Objective-C, Swift subclasses do not not inherit their superclass initializers by default. Swift’s approach prevents a situation in which a simple initializer from a superclass is automatically inherited by a more specialized subclass and is used to create a new instance of the subclass that is not fully or correctly initialized.
If you want your custom subclass to present one or more of the same initializers as its superclass—perhaps to perform some customization during initialization—you can provide an overriding implementation of the same initializer within your custom subclass.
If the initializer you are overriding is a designated initializer, you can override its implementation in your subclass and call the superclass version of the initializer from within your overriding version.
If the initializer you are overriding is a convenience initializer, your override must call another designated initializer from its own subclass, as per the rules described above in Initializer Chaining.

NOTE
Unlike methods, properties, and subscripts, you do not need to write the override
keyword when overriding an initializer.
構(gòu)造器自動(dòng)繼承
As mentioned above, subclasses do not not inherit their superclass initializers by default. However, superclass initializers are automatically inherited if certain conditions are met. In practice, this means that you do not need to write initializer overrides in many common scenarios, and can inherit your superclass initializers with minimal effort whenever it is safe to do so.
Assuming that you provide default values for any new properties you introduce in a subclass, the following two rules apply:
Rule 1
If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
Rule 2
If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.

These rules apply even if your subclass adds further convenience initializers.

NOTE
A subclass can implement a superclass designated initializer as a subclass convenience initializer as part of satisfying rule 2.
指定初始化和便捷初始化的語(yǔ)法
Designated initializers for classes are written in the same way as simple initializers for value types:

init(parameters) {
 statements
}

Convenience initializers are written in the same style, but with the convenience
keyword placed before the init
keyword, separated by a space:

convenience init(parameters) {
 statements
}

指定初始化和便捷初始化實(shí)戰(zhàn)
下面的例子演示的是指定構(gòu)造器罢绽,便捷構(gòu)造器和自動(dòng)構(gòu)造器繼承的實(shí)戰(zhàn)。例子中定義了三個(gè)類分別叫Food静盅,RecipeIngredient和ShoppingListItem良价,并給出了他們的繼承關(guān)系。
基類叫做Food蒿叠,是一個(gè)簡(jiǎn)單的類只有一個(gè)name屬性:

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

下圖就是Food類的構(gòu)造鏈:


類不存在成員逐一構(gòu)造器明垢,所以Food類提供了一個(gè)指定構(gòu)造器,使用參數(shù)name來(lái)完成初始化:

let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon"

init(name:String)構(gòu)造器就是Food類中的指定構(gòu)造器市咽,因?yàn)樗WC了每一個(gè)Food實(shí)例的屬性都被初始化了痊银。由于它沒(méi)有父類,所以不需要調(diào)用super.init()構(gòu)造器魂务。
Food類也提供了便捷構(gòu)造器init()曼验,這個(gè)構(gòu)造器沒(méi)有參數(shù)泌射,僅僅只是將name設(shè)置為了[Unnamed]:

let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]"

下一個(gè)類是Food的子類粘姜,叫做RecipeIngredient。這個(gè)類描述的是做飯時(shí)候的配料熔酷,包括一個(gè)數(shù)量屬性Int類型孤紧,然后定義了兩個(gè)構(gòu)造器:

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

下圖表示這兩個(gè)類的構(gòu)造鏈:

RecipeIngredient類有它自己的指定構(gòu)造器init(name: String, quantity:Int),用來(lái)創(chuàng)建一個(gè)新的RecipeIngredient實(shí)例拒秘。在這個(gè)指定構(gòu)造器中它調(diào)用了父類的指定構(gòu)造器init(name:String)号显。
然后它還有一個(gè)便捷構(gòu)造器,init(name)躺酒,它使用了同一個(gè)類中的指定構(gòu)造器押蚤。當(dāng)然它還包括一個(gè)繼承來(lái)的默認(rèn)構(gòu)造器init(),這個(gè)構(gòu)造器將使用RecipeIngredient中的init(name: String)構(gòu)造器羹应。
RecipeIngredient
also defines a convenience initializer, init(name: String)
, which is used to create aRecipeIngredient
instance by name alone. This convenience initializer assumes a quantity of 1
for anyRecipeIngredient
instance that is created without an explicit quantity. The definition of this convenience initializer makes RecipeIngredient
instances quicker and more convenient to create, and avoids code duplication when creating several single-quantity RecipeIngredient
instances. This convenience initializer simply delegates across to the class’s designated initializer.
Note that the init(name: String)
convenience initializer provided by RecipeIngredient
takes the same parameters as the init(name: String)
designated initializer from Food
. Even though RecipeIngredient
provides this initializer as a convenience initializer, RecipeIngredient
has nonetheless provided an implementation of all of its superclass’s designated initializers. Therefore, RecipeIngredient
automatically inherits all of its superclass’s convenience initializers too.
In this example, the superclass for RecipeIngredient
is Food
, which has a single convenience initializer calledinit()
. This initializer is therefore inherited by RecipeIngredient
. The inherited version of init()
functions in exactly the same way as the Food
version, except that it delegates to theRecipeIngredient
version of init(name: String)
rather than the Food
version.
上述三種構(gòu)造器都可以用來(lái)創(chuàng)建RecipeIngredient實(shí)例:

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

最后一個(gè)類是ShoppingListItem繼承自RecipeIngredient揽碘,它又包括了另外兩個(gè)屬性,是否已購(gòu)買purchased,描述description雳刺,描述本身還是一個(gè)計(jì)算屬性:

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

注意:ShoppingListItem沒(méi)有定義構(gòu)造器來(lái)初始化purchased的值劫灶,因?yàn)槊總€(gè)商品在買之前purchased都是默認(rèn)被設(shè)置為沒(méi)有被購(gòu)買的。
因?yàn)镾hoppingListItem沒(méi)有提供其他構(gòu)造器掖桦,那么它就完全繼承了父類的構(gòu)造器本昏,用下圖可以說(shuō)明:

你可以在創(chuàng)建ShoppingListItem實(shí)例時(shí)使用所有的繼承構(gòu)造器:

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 yes
// 1 x bacon no
// 6 x eggs no

通過(guò)輸出可以看出所有的實(shí)例在創(chuàng)建的時(shí)候,屬性的默認(rèn)值都被正確的初始化了枪汪。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末涌穆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子雀久,更是在濱河造成了極大的恐慌蒲犬,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岸啡,死亡現(xiàn)場(chǎng)離奇詭異原叮,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)巡蘸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門奋隶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人悦荒,你說(shuō)我怎么就攤上這事唯欣。” “怎么了搬味?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵境氢,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我碰纬,道長(zhǎng)萍聊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任悦析,我火速辦了婚禮寿桨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘强戴。我一直安慰自己亭螟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布骑歹。 她就那樣靜靜地躺著预烙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪道媚。 梳的紋絲不亂的頭發(fā)上扁掸,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天欢嘿,我揣著相機(jī)與錄音,去河邊找鬼也糊。 笑死炼蹦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的狸剃。 我是一名探鬼主播掐隐,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼钞馁!你這毒婦竟也來(lái)了虑省?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤僧凰,失蹤者是張志新(化名)和其女友劉穎探颈,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體训措,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伪节,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绩鸣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怀大。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖呀闻,靈堂內(nèi)的尸體忽然破棺而出化借,到底是詐尸還是另有隱情,我是刑警寧澤捡多,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布蓖康,位于F島的核電站,受9級(jí)特大地震影響垒手,放射性物質(zhì)發(fā)生泄漏蒜焊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一淫奔、第九天 我趴在偏房一處隱蔽的房頂上張望山涡。 院中可真熱鬧堤结,春花似錦唆迁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至瘾带,卻和暖如春鼠哥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工朴恳, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抄罕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓于颖,卻偏偏與公主長(zhǎng)得像呆贿,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子森渐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

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

  • 原文地址:http://huizhao.win/2016/11/13/swift-init/ 從 Objectiv...
    趙大老板閱讀 34,341評(píng)論 11 120
  • 二溪閱讀 145評(píng)論 3 8
  • 一直不受我們喜歡的他做入,昨天晚上講話時(shí)竟流淚了。他說(shuō)“你們知道同衣,我為什么對(duì)你們這么嚴(yán)格嗎竟块?我,只是個(gè)屇推耄科畢業(yè)的浪秘,并沒(méi)...
    15個(gè)字符閱讀 160評(píng)論 0 0
  • 單位每年都組織體檢,我也沒(méi)有當(dāng)回事埠况,反正年年身體倍棒吃嘛嘛香秫逝。體檢結(jié)果出來(lái)了,我也沒(méi)有著急去取询枚,拖了很久之后...
    c61c65d3a41e閱讀 247評(píng)論 1 0