初始化是準(zhǔn)備使用的類匿刮,結(jié)構(gòu)或枚舉實例的過程僧凰。此過程涉及為該實例上的每個存儲屬性設(shè)置初始值,并執(zhí)行新實例準(zhǔn)備使用之前所需的任何其他設(shè)置或初始化熟丸。
您可以通過定義初始值設(shè)定項來實現(xiàn)此初始化過程训措,初始值設(shè)定項類似于可以調(diào)用以創(chuàng)建特定類型新實例的特殊方法。與Objective-C初始值設(shè)定項不同光羞,Swift初始值設(shè)定項不會返回值绩鸣。它們的主要作用是確保類型的新實例在首次使用之前正確初始化。
類的實例還可以實現(xiàn)一個deinitializer纱兑,它在釋放該類的實例之前執(zhí)行任何自定義清除呀闻。
設(shè)置存儲屬性的初始值
類和結(jié)構(gòu)必須在創(chuàng)建該類或結(jié)構(gòu)的實例時將其所有存儲的屬性設(shè)置為適當(dāng)?shù)某跏贾怠4鎯Φ膶傩圆荒芴幱诓淮_定狀態(tài)潜慎。
您可以在初始化程序中為存儲的屬性設(shè)置初始值捡多,或通過將默認屬性值分配為屬性定義的一部分來設(shè)置初始值。
當(dāng)您為存儲的屬性分配默認值铐炫,或在初始化程序中設(shè)置其初始值時垒手,將直接設(shè)置該屬性的值,而無需調(diào)用任何屬性觀察器倒信。
默認屬性值
您可以從初始化程序中設(shè)置存儲屬性的初始值科贬,或者指定默認屬性值作為屬性聲明的一部分。您可以通過在定義屬性時為其分配初始值來指定默認屬性值鳖悠。
如果屬性始終采用相同的初始值榜掌,請?zhí)峁┠J值,而不要在初始化程序中設(shè)置值乘综。最終結(jié)果是相同的憎账,但是默認值將屬性的初始化與其聲明緊密聯(lián)系在一起。它使初始化程序更短瘾带,更清晰鼠哥,并使您能夠從其默認值推斷屬性的類型熟菲。默認值還使您更容易利用默認初始化程序和初始化程序繼承。
自定義初始化
您可以使用輸入?yún)?shù)和可選屬性類型朴恳,或通過在初始化期間分配常量屬性來自定義初始化過程抄罕。
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
參數(shù)名稱和參數(shù)標(biāo)簽
與函數(shù)和方法參數(shù)一樣,初始化參數(shù)可以具有在初始化程序的主體內(nèi)使用的參數(shù)名稱和在調(diào)用初始化程序時使用的參數(shù)標(biāo)簽于颖。
但是呆贿,初始化函數(shù)在其括號前沒有以函數(shù)和方法那樣的方式標(biāo)識函數(shù)的名稱。因此森渐,初始化器參數(shù)的名稱和類型在確定應(yīng)調(diào)用哪個初始化器中起著特別重要的作用做入。因此,如果不提供初始化功能同衣,Swift會為初始化程序中的每個參數(shù)提供一個自動參數(shù)標(biāo)簽竟块。
struct Color {
? ? let red, green, blue: Double
? ? init(red:Double, green:Double, blue:Double) {
? ? ? ? self.red? = red
? ? ? ? self.green= green
? ? ? ? self.blue? = blue
? ? }
? ? init(white:Double) {
? ? ? ? red? = white
? ? ? ? green= white
? ? ? ? blue? = white
? ? }
}
不帶參數(shù)標(biāo)簽的初始化參數(shù)
如果您不想為初始化參數(shù)使用參數(shù)標(biāo)簽,請為該參數(shù)寫下劃線(_)而不是顯式參數(shù)標(biāo)簽耐齐,以覆蓋默認行為浪秘。
可選屬性類型
如果您的自定義類型具有在邏輯上允許為“無值”的存儲屬性(可能是因為在初始化期間無法設(shè)置其值,或者是因為在以后的某個時候允許其無值)埠况,請使用可選類型耸携。可選類型的屬性將使用值自動初始化nil辕翰,表明該屬性在初始化過程中故意具有“沒有值”夺衍。
在初始化期間分配常量屬性
您可以在初始化期間的任何時候為常量屬性分配一個值,只要在初始化完成時將其設(shè)置為確定值即可喜命。為常數(shù)屬性分配值后沟沙,就無法再對其進行修改。
對于類實例壁榕,只能在引入常量的類的初始化期間對其進行修改尝胆。子類不能修改它。
默認初始化器
迅速提供了一個默認初始值對于所有其屬性提供缺省值护桦,并且不提供至少一個初始值設(shè)定本身的任何結(jié)構(gòu)或類。默認初始化程序僅創(chuàng)建一個新實例煎娇,并將其所有屬性設(shè)置為其默認值二庵。
類繼承和初始化
在初始化期間,必須為類的所有存儲屬性(包括該類從其超類繼承的所有屬性)分配一個初始值缓呛。
Swift為類類型定義了兩種初始化器催享,以幫助確保所有存儲的屬性都接收初始值。這些被稱為指定的初始化程序和便捷初始化程序哟绊。
指定的初始化程序和便利性初始化程序
指定的初始化器是類的主要初始化器因妙。指定的初始化程序?qū)⑼耆跏蓟擃愐氲乃袑傩裕⒄{(diào)用適當(dāng)?shù)某惓跏蓟绦蛞岳^續(xù)超類鏈中的初始化過程。
類往往只有很少的指定初始化程序攀涵,而一個類只有一個很常見铣耘。指定的初始化程序是“漏斗”點,通過它們進行初始化以故,并通過該“漏斗”點蜗细,初始化過程在超類鏈中繼續(xù)進行。
規(guī)則1
指定的初始化程序必須從其直接超類調(diào)用指定的初始化程序怒详。
規(guī)則二
便捷初始化程序必須從同一類調(diào)用另一個初始化程序炉媒。
規(guī)則三
便捷初始化程序必須最終調(diào)用指定的初始化程序。
在這里昆烁,超類具有一個指定的初始值設(shè)定項和兩個便利的初始值設(shè)定項吊骤。一個便利初始化程序調(diào)用另一個便利初始化程序,后者又調(diào)用單個指定的初始化程序静尼。這從上方滿足規(guī)則2和3白粉。超類本身沒有其他超類,因此規(guī)則1不適用茅郎。
該圖中的子類具有兩個指定的初始化程序和一個便捷的初始化程序蜗元。便捷初始化程序必須調(diào)用兩個指定的初始化程序之一,因為它只能調(diào)用同一類中的另一個初始化程序系冗。這從上方滿足規(guī)則2和3奕扣。兩個指定的初始值設(shè)定項都必須從超類調(diào)用單個指定的初始值設(shè)定項,才能滿足上面的規(guī)則1掌敬。
二段式初始化
Swift中的類初始化是一個分為兩個階段的過程惯豆。在第一階段,每個存儲的屬性都由引入它的類分配一個初始值奔害。一旦確定了每個存儲屬性的初始狀態(tài)楷兽,便開始第二階段,并且在認為新實例可以使用之前华临,每個類都有機會自定義其存儲屬性芯杀。
Swift的兩階段初始化過程類似于Objective-C中的初始化。主要區(qū)別在于雅潭,在階段1中揭厚,Objective-C為每個屬性分配零或空值(例如0或nil)。Swift的初始化流程更加靈活扶供,因為它可以讓您設(shè)置自定義初始值筛圆,并且可以處理有效值0或nil無效值的類型。Swift的編譯器執(zhí)行四項有用的安全檢查椿浓,以確保兩階段初始化完成且沒有錯誤:
安全檢查1
指定的初始值設(shè)定項必須確保由其類引入的所有屬性在委托給超類初始值設(shè)定項之前都已初始化太援。
如上所述闽晦,僅在知道對象所有存儲屬性的初始狀態(tài)后,才認為該對象的內(nèi)存已完全初始化提岔。為了滿足此規(guī)則仙蛉,指定的初始值設(shè)定項必須確保在傳遞鏈之前初始化其所有屬性。
安全檢查2
在將值分配給繼承的屬性之前唧垦,指定的初始值設(shè)定項必須委托一個超類初始值設(shè)定項捅儒。如果沒有,則超類將為其指定的初始化程序分配的新值覆蓋其自身的初始化振亮。
安全檢查3
便利初始化程序必須在將值分配給任何屬性(包括由同一類定義的屬性)之前委托給另一個初始化程序巧还。如果不是,便利初始化程序分配的新值將被其自己類的指定初始化程序覆蓋坊秸。
安全檢查4
在初始化self的第一階段完成之后麸祷,初始化程序才能調(diào)用任何實例方法,讀取任何實例屬性的值或?qū)⑵浞Q為值褒搔。
在第一階段結(jié)束之前阶牍,該類實例并不完全有效。一旦在第一階段結(jié)束時知道類實例是有效的星瘾,就只能訪問屬性走孽,并且只能調(diào)用方法。
根據(jù)上述四個安全檢查琳状,以下是兩階段初始化如何進行:
階段1
指定的或便捷的初始化程序在類上調(diào)用磕瓷。
分配該類的新實例的內(nèi)存。內(nèi)存尚未初始化念逞。
該類的指定初始化程序確認該類引入的所有存儲屬性都具有值±常現(xiàn)在已初始化這些存儲屬性的內(nèi)存。
指定的初始值設(shè)定項移交給超類初始值設(shè)定項翎承,以為其自身的存儲屬性執(zhí)行相同的任務(wù)硕盹。
這將繼續(xù)類繼承鏈,直到到達鏈的頂部叨咖。
一旦到達鏈的頂部瘩例,并且鏈中的最后一個類已確保其所有存儲的屬性都具有值,則實例的內(nèi)存被視為已完全初始化甸各,并且階段1已完成仰剿。
階段2
從鏈的頂部向下追溯,鏈中的每個指定的初始化程序都可以選擇進一步自定義實例痴晦。初始化程序現(xiàn)在可以訪問self并可以修改其屬性,調(diào)用其實例方法琳彩,等等誊酌。
最后部凑,鏈中的所有便利初始化程序都可以選擇自定義實例并使用self。
初始化程序的繼承和覆蓋
與Objective-C中的子類不同碧浊,Swift子類默認情況下不繼承其超類初始化器涂邀。Swift的方法可防止出現(xiàn)以下情況:超類中的簡單初始化程序被更專門的子類繼承,并用于創(chuàng)建未完全或正確初始化的子類的新實例箱锐。如果希望自定義子類提供與其父類相同的一個或多個相同的初始化器比勉,則可以在子類中提供這些初始化器的自定義實現(xiàn)。當(dāng)編寫與超類指定的初始值設(shè)定項匹配的子類初始值設(shè)定項時驹止,實際上是在提供該指定的初始值設(shè)定項的替代浩聋。因此,必須override在子類的初始化程序定義之前編寫修飾符臊恋。即使您要覆蓋自動提供的默認初始化程序衣洁,這也是正確的。
與覆蓋屬性抖仅,方法或下標(biāo)一樣坊夫,override修飾符的存在會提示Swift檢查超類是否具有匹配的指定初始化器要被覆蓋,并驗證是否已按預(yù)期指定了覆蓋初始化器的參數(shù)撤卢。override重寫超類指定的初始值設(shè)定項時环凿,您始終會編寫修飾符,即使您的子類對初始值設(shè)定項的實現(xiàn)是便利的初始值設(shè)定項也是如此放吩。
相反智听,如果您編寫與超類便捷性初始化程序匹配的子類初始值設(shè)定項,您的子類將永遠無法直接調(diào)用該超類便利性初始化程序屎慢。因此瞭稼,您的子類(嚴格地說)沒有提供超類初始值設(shè)定項的替代。因此腻惠,override在提供超類便捷初始化程序的匹配實現(xiàn)時环肘,您無需編寫修飾符。
自動初始化程序繼承
如上所述集灌,子類默認情況下不繼承其超類初始化器悔雹。但是,如果滿足某些條件欣喧,則會自動繼承超類初始化器腌零。實際上,這意味著您無需在許多常見情況下編寫初始化程序覆蓋唆阿,并且可以在安全的情況下以最小的努力繼承超類初始化程序益涧。
假設(shè)您為子類中引入的任何新屬性提供默認值,則適用以下兩個規(guī)則:
規(guī)則一
如果您的子類未定義任何指定的初始值設(shè)定項驯鳖,它將自動繼承其所有超類指定的初始值設(shè)定項闲询。
規(guī)則二
如果您的子類提供了其所有超類指定初始化器的實現(xiàn)(通過按規(guī)則1繼承它們久免,或通過提供自定義實現(xiàn)作為其定義的一部分),那么它將自動繼承所有超類便利性初始化器扭弧。
即使您的子類添加了進一步的便利初始化程序阎姥,這些規(guī)則也適用。