1.設(shè)置存儲(chǔ)屬性的初始值
在創(chuàng)建類(lèi)或結(jié)構(gòu)實(shí)例時(shí)茧痒,類(lèi)和結(jié)構(gòu)必須為所有存儲(chǔ)屬性設(shè)置初始值旺订。存儲(chǔ)屬性不能保留在不確定的狀態(tài)区拳。
可以在構(gòu)造器中設(shè)置存儲(chǔ)屬性的初始值,也可以在屬性聲明時(shí)設(shè)置默認(rèn)值笆凌。
注意:
將默認(rèn)值分配給存儲(chǔ)屬性或在初始化程序中設(shè)置其初始值時(shí)乞而,將直接設(shè)置該屬性的值晦闰,而不調(diào)用任何屬性觀察者跪妥。
(1).構(gòu)造器
可以調(diào)用構(gòu)造器創(chuàng)建特定類(lèi)型的新實(shí)例眉撵。無(wú)參構(gòu)造器是一種最簡(jiǎn)單的初始化方法纽疟,使用init關(guān)鍵字:
init() {
// perform some initialization here
}
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit")
// Prints "The default temperature is 32.0° Fahrenheit"
(2).默認(rèn)的屬性值
可以在構(gòu)造器中設(shè)置存儲(chǔ)屬性的初始值,如上所示蟆肆⊙坠Γ或者蛇损,將默認(rèn)屬性值指定為屬性聲明的一部分。通過(guò)在定義屬性時(shí)為其指定初始值更啄,可以指定默認(rèn)屬性值锈死。
struct Fahrenheit {
var temperature = 32.0
}
2.自定義初始化
可以使用輸入?yún)?shù)和可選屬性類(lèi)型自定義初始化過(guò)程待牵,或者在初始化期間分配常量屬性,如以下部分所述贰拿。
(1).初始化參數(shù)
可以提供初始化參數(shù)作為構(gòu)造器定義的一部分膨更,來(lái)定義在自定義初始化過(guò)程中值的類(lèi)型和名稱(chēng)珍德。初始化參數(shù)與函數(shù)和方法參數(shù)具有相同的功能和語(yǔ)法锈候。
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
(2).參數(shù)名稱(chēng)和參數(shù)標(biāo)簽
與函數(shù)和方法參數(shù)一樣,初始化參數(shù)既可以具有在構(gòu)造器體內(nèi)使用的參數(shù)名稱(chēng)获列,也可以具有在調(diào)用構(gòu)造器時(shí)使用的參數(shù)標(biāo)簽蛛倦。
構(gòu)造器參數(shù)的名稱(chēng)和類(lèi)型在確定調(diào)用哪個(gè)構(gòu)造器時(shí)起著特別重要的作用及皂。因此验烧,若沒(méi)有給構(gòu)造器中的每個(gè)參數(shù)提供參數(shù)標(biāo)簽若治,則Swift會(huì)給每個(gè)參數(shù)提供自動(dòng)參數(shù)標(biāo)簽端幼。
truct 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
}
}
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(white: 0.5)
let veryGreen = Color(0.0, 1.0, 0.0)
// this reports a compile-time error - argument labels are required
(3).無(wú)參數(shù)標(biāo)簽的初始化參數(shù)
如果不想為構(gòu)造器參數(shù)提供參數(shù)標(biāo)簽,則可以在參數(shù)名稱(chēng)添加下劃線_
滑进。
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
(4).可選屬性類(lèi)型
可以在自定義的類(lèi)型中使用可選的存儲(chǔ)屬性阴汇■昙模可選類(lèi)型的屬性將自動(dòng)初始化為值nil,表示該屬性在初始化期間無(wú)值未斑。
class SurveyQuestion {
var text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
cheeseQuestion.ask()
// Prints "Do you like cheese?"
cheeseQuestion.response = "Yes, I do like cheese."
(5).在初始化過(guò)程中分配常量屬性
只要在初始化完成時(shí)將其設(shè)置為確定值,就可以在初始化期間的任何時(shí)刻為常量屬性賦值芽突。一旦為常量屬性賦值寞蚌,就無(wú)法進(jìn)一步修改它。
注意:
對(duì)于類(lèi)實(shí)例艘刚,只能在類(lèi)初始化期間修改常量屬性。而且常量屬性不能被子類(lèi)修改秋度。
class SurveyQuestion {
let text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
let beetsQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask()
// Prints "How about beets?"
beetsQuestion.response = "I also like beets. (But not with cheese.)"
3.默認(rèn)構(gòu)造器
- 類(lèi)的默認(rèn)構(gòu)造器
當(dāng)一個(gè)類(lèi)的所有屬性都有默認(rèn)值,且沒(méi)有提供任何構(gòu)造器鲸拥,則系統(tǒng)提構(gòu)一個(gè)默認(rèn)的無(wú)參構(gòu)造器刑赶。
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
- 結(jié)構(gòu)體的默認(rèn)構(gòu)造器
如果結(jié)構(gòu)類(lèi)型沒(méi)有定義任何自己的構(gòu)造器金踪,則它們會(huì)自動(dòng)接收一個(gè)全能初始化器胡岔。與默認(rèn)構(gòu)造器不同,即使結(jié)構(gòu)體的存儲(chǔ)屬性沒(méi)有默認(rèn)值怨咪,該結(jié)構(gòu)體也會(huì)接收一個(gè)全能初始化器。
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
4.值類(lèi)型的構(gòu)造器委托
構(gòu)造器可以調(diào)用其他構(gòu)造器來(lái)執(zhí)行實(shí)例初始化的一部分匠楚。此過(guò)程稱(chēng)為構(gòu)造器委托典徘,可避免跨多個(gè)構(gòu)造器拷貝代碼逮诲。
對(duì)于值類(lèi)型和類(lèi)類(lèi)型裆甩,構(gòu)造器委托的工作原理和允許的委托形式的規(guī)則是不同的嗤栓。值類(lèi)型(結(jié)構(gòu)和枚舉)不支持繼承叨叙,因此它們的構(gòu)造器委托過(guò)程相對(duì)簡(jiǎn)單擂错,因?yàn)樗鼈冎荒芪薪o它們自己提供的另一個(gè)構(gòu)造器钮呀。然而,類(lèi)可以從其他類(lèi)繼承昨凡。這意味著類(lèi)有額外的職責(zé)爽醋,以確保在初始化期間為它們繼承的所有存儲(chǔ)屬性分配適當(dāng)?shù)闹怠?br>
如果為值類(lèi)型提供了一個(gè)自定義構(gòu)造器,則無(wú)法再訪問(wèn)該類(lèi)型的默認(rèn)構(gòu)造器便脊。
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)
}
}
let basicRect = Rect()
// basicRect's origin is (0.0, 0.0) and its size is (0.0, 0.0)
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)
5.類(lèi)繼承和初始化
類(lèi)的所有存儲(chǔ)屬性(包括從父類(lèi)繼承的存儲(chǔ)屬性)都必須在初始化期間賦值子房。Swift為類(lèi)類(lèi)型定義了兩種構(gòu)造器就轧,以確保所有存儲(chǔ)屬性都能被初始化证杭。這兩種構(gòu)造器是指定構(gòu)造器
和便利構(gòu)造器
。
(1).指定構(gòu)造器和便利構(gòu)造器
- 指定構(gòu)造器是類(lèi)的主要構(gòu)造器妒御。它可以完全初始化該類(lèi)引入的所有屬性解愤,并調(diào)用適當(dāng)?shù)某?lèi)構(gòu)造器,以繼續(xù)超類(lèi)鏈上的初始化過(guò)程乎莉。
- 每個(gè)類(lèi)必須至少有一個(gè)指定構(gòu)造器送讲。
- 便利構(gòu)造器是次要的,支持類(lèi)的構(gòu)造器惋啃。
(2).指定構(gòu)造器和便利構(gòu)造器的語(yǔ)法格式
- 指定構(gòu)造器
init(parameters) {
statements
}
- 便利構(gòu)造器
convenience init(parameters) {
statements
}
(3).類(lèi)類(lèi)型的構(gòu)造器委托
為了簡(jiǎn)化指定構(gòu)造器和便利構(gòu)造器之間的關(guān)系哼鬓,為Swift對(duì)構(gòu)造器之間的委托調(diào)用應(yīng)用以下三個(gè)規(guī)則:
- 規(guī)則1 :一個(gè)指定構(gòu)造器必須從其直接超類(lèi)中調(diào)用一個(gè)指定構(gòu)造器
- 規(guī)則2:一個(gè)便利構(gòu)造器必須調(diào)用同一個(gè)類(lèi)中的另一個(gè)構(gòu)造器。
- 規(guī)則3:便利構(gòu)造器最終必須調(diào)用指定構(gòu)造器边灭。
簡(jiǎn)單來(lái)說(shuō): - 指定構(gòu)造器必須總是向上委托异希。
- 便利構(gòu)造器必須總是橫向委托。
規(guī)則如下圖所示:
(4).兩階段初始化
在Swift中,類(lèi)的初始化分兩個(gè)階段:
- 第一階段:每個(gè)存儲(chǔ)屬性都由引入它的類(lèi)分配一個(gè)初始值惰帽。
- 第二階段:當(dāng)每個(gè)存儲(chǔ)屬性的初始化狀態(tài)都被確定之后憨降,開(kāi)始第二階段。在新實(shí)例被認(rèn)為可以使用之前该酗,每個(gè)類(lèi)都有機(jī)會(huì)進(jìn)一步自定義它的存儲(chǔ)屬性授药。
兩階段初始化可以防止每個(gè)屬性值在初始化之前被訪問(wèn);并能夠防止屬性值被另一個(gè)構(gòu)造器意外地設(shè)置為其他值。
Swift的編譯器執(zhí)行四個(gè)有用的安全檢查悔叽,以確保完成兩階段初始化而不會(huì)出現(xiàn)錯(cuò)誤:
-
安全檢測(cè)一:
指定構(gòu)造器必須確保其類(lèi)引入的所有屬性在向上委托給超類(lèi)構(gòu)造器之前都被初始化航邢。
如上所述,只有當(dāng)對(duì)象的所有存儲(chǔ)屬性的初始狀態(tài)已知時(shí)骄蝇,才認(rèn)為對(duì)象的內(nèi)存是完全初始化的膳殷。為了滿足這一規(guī)則,指定構(gòu)造器必須確保在傳遞到繼承鏈上之前對(duì)其自身的所有屬性進(jìn)行初始化九火。 -
安全檢測(cè)二:
在給繼承屬性賦值之前赚窃,指定構(gòu)造器必須先調(diào)用超類(lèi)構(gòu)造器。若沒(méi)有岔激,指定構(gòu)造器賦的新值將被超類(lèi)覆蓋勒极,作為其初始化的一部分。 -
安全檢測(cè)三:
在為任何屬性(包括由同一個(gè)類(lèi)定義的屬性)賦值之前虑鼎,便利構(gòu)造器必須委托給另一個(gè)構(gòu)造器辱匿。如果沒(méi)有,那么便利構(gòu)造器賦的新值將被其類(lèi)的指定構(gòu)造器覆蓋炫彩。 -
安全檢測(cè)四:
只有當(dāng)初始化的第一階段結(jié)束之后匾七, 構(gòu)造器才能調(diào)用實(shí)例方法,讀取實(shí)例屬性的值江兢,將self作為值引用昨忆。
基于上面的四個(gè)安全檢測(cè),以下是兩階段初始化的過(guò)程:
Phase 1
- 在類(lèi)中調(diào)用指定構(gòu)造器杉允,或便利構(gòu)造器邑贴;
- 給類(lèi)的新實(shí)例對(duì)象分配內(nèi)存。分配的內(nèi)存尚未初始化叔磷;
- 該類(lèi)的指定構(gòu)造器確認(rèn)由該類(lèi)引入的所有存儲(chǔ)屬性都有一個(gè)值÷<荩現(xiàn)在分配給這些存儲(chǔ)屬性的內(nèi)存被初始化;
- 指定構(gòu)造器交給超類(lèi)的構(gòu)造器改基,以對(duì)其自身的存儲(chǔ)屬性執(zhí)行相同的任務(wù)繁疤;
- 這將沿著繼承鏈繼續(xù)向上,直到繼承鏈頂端寥裂;
- 一旦到達(dá)繼承鏈頂端嵌洼,繼承鏈中的最后一個(gè)類(lèi)要確保它所有的存儲(chǔ)屬性都有值,則實(shí)例對(duì)象的內(nèi)存被認(rèn)為完全初始化封恰。第一階段完成
Phase 1示例圖:
如安全檢測(cè)一所示,指定構(gòu)造器確保子類(lèi)中的所有屬性都有值。然后低飒,它調(diào)用父類(lèi)指定構(gòu)造器繼續(xù)繼承鏈上的初始化许昨。
父類(lèi)構(gòu)造器確保父類(lèi)中的所有屬性都有值。沒(méi)有需要初始化的父類(lèi)褥赊,因此不需要進(jìn)一步的委托糕档。
只要超類(lèi)的所有屬性都有一個(gè)初始值,它的內(nèi)存就被認(rèn)為是完全初始化的拌喉,階段1就完成了速那。
Phase 2 - 從繼承鏈的頂部向下執(zhí)行,鏈中的每個(gè)指定構(gòu)造器都可以進(jìn)一步選擇自定義實(shí)例尿背。構(gòu)造器現(xiàn)在可以訪問(wèn)self端仰,可以修改self的屬性,調(diào)用它的實(shí)例方法田藐,等等荔烧。
-
最后,鏈中的任何便利構(gòu)造器都可以選擇自定義實(shí)例汽久,并可以使用self鹤竭。
Phase 2示例圖:
一旦父類(lèi)指定構(gòu)造器完成景醇,子類(lèi)的指定構(gòu)造器就可以執(zhí)行額外的自定義操作(盡管它也沒(méi)必這樣做)诺擅。
最后,一旦子類(lèi)的指定構(gòu)造器完成啡直,最初調(diào)用的便利構(gòu)造器就可以執(zhí)行額外的自定義操作烁涌。
(5).構(gòu)造器的繼承和重寫(xiě)
- 與Objective-C中的子類(lèi)不同,Swift的子類(lèi)默認(rèn)不繼承父類(lèi)構(gòu)造器酒觅。
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
let vehicle = Vehicle()
print("Vehicle: \(vehicle.description)")
// Vehicle: 0 wheel(s)
class Bicycle: Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}
let bicycle = Bicycle()
print("Bicycle: \(bicycle.description)")
// Bicycle: 2 wheel(s)
(6).自動(dòng)構(gòu)造器繼承
如上所述撮执,子類(lèi)默認(rèn)情況下不會(huì)繼承父類(lèi)構(gòu)造器。但是舷丹,如果滿足某些條件抒钱,超類(lèi)構(gòu)造器將自動(dòng)被繼承。在實(shí)踐中颜凯,這意味著不需要在許多常見(jiàn)場(chǎng)景中重寫(xiě)構(gòu)造器谋币,只要這樣做是安全的,就可以以最小的代價(jià)繼承父類(lèi)構(gòu)造器症概。
假設(shè)為子類(lèi)中引入的任何新屬性都提供了默認(rèn)值蕾额,則適用以下兩條規(guī)則:
-
規(guī)則一
:如果你的子類(lèi)沒(méi)有定義任何指定構(gòu)造器,它會(huì)自動(dòng)繼承所有超類(lèi)的指定構(gòu)造器彼城。 -
規(guī)則二
:如果你的子類(lèi)提供了其所有超類(lèi)指定構(gòu)造器的實(shí)現(xiàn)------通過(guò)按照規(guī)則1繼承它們诅蝶,或者通過(guò)提供自定義實(shí)現(xiàn)作為其定義的一部分------那么它將自動(dòng)繼承所有超類(lèi)的便利構(gòu)造器退个。
子類(lèi)可以實(shí)現(xiàn)一個(gè)超類(lèi)的指定構(gòu)造器作為子類(lèi)的便利構(gòu)造器,來(lái)滿足規(guī)則2 调炬。
(7).指定和便利構(gòu)造器的應(yīng)用
下面的示例顯示了指定構(gòu)造器语盈、方便構(gòu)造器和自動(dòng)構(gòu)造器繼承的應(yīng)用。該示例定義了一個(gè)由三個(gè)類(lèi)組成的層次結(jié)構(gòu)缰泡,分別是Food刀荒、RecipeIngredient和ShoppingListItem,并演示了它們的構(gòu)造器如何相互作用棘钞。
層次結(jié)構(gòu)中的基類(lèi)稱(chēng)為Food缠借,這是一個(gè)簡(jiǎn)單的類(lèi),用來(lái)封裝Food的名稱(chēng)武翎。Food類(lèi)引入了一個(gè)名為name的字符串屬性烈炭,并提供了兩個(gè)用于創(chuàng)建Food實(shí)例的構(gòu)造器:
class Food {
var name: String
//指定構(gòu)造器
init(name: String) {
self.name = name
}
//便利構(gòu)造器
convenience init() {
self.init(name: "[Unnamed]")
}
}
下圖顯示了Food類(lèi)的初始化鏈:類(lèi)沒(méi)有默認(rèn)的全能構(gòu)造器,因此Food類(lèi)提供了一個(gè)指定構(gòu)造器宝恶,它接受一個(gè)名為name的參數(shù)符隙。此構(gòu)造器可用于創(chuàng)建具有特定名稱(chēng)的新Food實(shí)例:
let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon"
Food類(lèi)中的init(name:String)
構(gòu)造器是指定構(gòu)造器,因?yàn)樗_保了新Food實(shí)例的所有存儲(chǔ)屬性都被完全初始化垫毙。Food類(lèi)沒(méi)有超類(lèi)霹疫,因此init(name:String)
構(gòu)造器不需要調(diào)用super.init()
來(lái)完成初始化。
Food類(lèi)還提供了一個(gè)沒(méi)有參數(shù)的便利構(gòu)造器init()
综芥,init()
構(gòu)造器調(diào)用Food類(lèi)的指定構(gòu)造器丽蝎。
let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]"
第二個(gè)類(lèi)RecipeIngredient
是Food
的子類(lèi)。RecipeIngredient
類(lèi)模擬烹飪配方中的一個(gè)成分膀藐。它引入了一個(gè)名為quantity
的整型屬性(除了從Food繼承的name
屬性之外)屠阻,并定義了兩個(gè)用于創(chuàng)建RecipeIngredient
實(shí)例的構(gòu)造器:
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
類(lèi)的初始化鏈:
let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
類(lèi)ShoppingListItem
是RecipeIngredient
的子類(lèi)。ShoppingListItem
類(lèi)模擬了出現(xiàn)在購(gòu)物清單中的配方配料额各。
class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name)"
output += purchased ? " ?" : " ?"
return output
}
}
因?yàn)?code>ShoppingListItem類(lèi)為它引入的所有屬性提供了默認(rèn)值国觉,并且它本身沒(méi)有定義任何構(gòu)造器,所以ShoppingListItem
自動(dòng)從它的超類(lèi)繼承了所有指定構(gòu)造器和方便構(gòu)造器虾啦。
下圖顯示了三個(gè)類(lèi)的整體初始化鏈:
ShoppingListItem
實(shí)例:
var breakfastList = [
ShoppingListItem(),
ShoppingListItem(name: "Bacon"),
ShoppingListItem(name: "Eggs", quantity: 6),
]
breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true
for item in breakfastList {
print(item.description)
}
// 1 x Orange juice ?
// 1 x Bacon ?
// 6 x Eggs ?
6.可失敗構(gòu)造器
在類(lèi)麻诀,結(jié)構(gòu)體和枚舉的初始化過(guò)程中,由于傳入無(wú)效的初始化參數(shù)值傲醉,或缺少某種所需的外部資源蝇闭,或阻止初始化成功的其他條件等,都會(huì)導(dǎo)致初始化失敗硬毕。為了處理這些可能失敗的初始化條件呻引,通常會(huì)定義一個(gè)或多個(gè)可失敗的構(gòu)造器作為類(lèi),結(jié)構(gòu)體或枚舉定義的一部分昭殉。通過(guò)在關(guān)鍵字init
后面添加問(wèn)好(即init?
)定義可失敗構(gòu)造器苞七∶晔兀可失敗構(gòu)造器創(chuàng)建一個(gè)其初始化類(lèi)型的可選值挪丢,初始化失敗時(shí)返回nil
蹂风。
注意:
不能定義具有相同參數(shù)類(lèi)型和名稱(chēng)的可失敗構(gòu)造器和不可失敗構(gòu)造器。
如下例所示:結(jié)構(gòu)體Animal
含有一個(gè)常量字符串屬性species
,和一個(gè)含有參數(shù) species
的可失敗構(gòu)造器乾蓬。若參數(shù)為空惠啄,則會(huì)觸發(fā)初始化失敗任内;否則撵渡,設(shè)置Animal
屬性的值,初始化成功:
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
使用可失敗構(gòu)造器初始化一個(gè)Animal
實(shí)例死嗦,并檢查初始化是否成功:
let someCreature = Animal(species: "Giraffe")
// someCreature is of type Animal?, not Animal
if let giraffe = someCreature {
print("An animal was initialized with a species of \(giraffe.species)")
}
// Prints "An animal was initialized with a species of Giraffe"
如果將空字符串值傳遞給可失敗構(gòu)造器的species
參數(shù)趋距,則構(gòu)造器會(huì)觸發(fā)初始化失敗:
let anonymousCreature = Animal(species: "")
// anonymousCreature is of type Animal?, not Animal
if anonymousCreature == nil {
print("The anonymous creature could not be initialized")
}
// Prints "The anonymous creature could not be initialized"
(1).枚舉的可失敗構(gòu)造器
可使用可失敗構(gòu)造器根據(jù)一個(gè)或多個(gè)參數(shù)來(lái)選擇合適的枚舉成員越除。 如果提供的參數(shù)與適當(dāng)?shù)拿杜e成員不匹配节腐,則構(gòu)造器可能會(huì)失敗。
如下例所示:
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
}
}
}
可以使用這個(gè)可失敗構(gòu)造器為三種可能的狀態(tài)選擇適當(dāng)?shù)拿杜e情況摘盆,如果參數(shù)與以下三種狀態(tài)之一不匹配時(shí)會(huì)導(dǎo)致初始化失斠砣浮:
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// Prints "This is a defined temperature unit, so initialization succeeded."
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
(2).具有原始值枚舉的可失敗構(gòu)造器
具有原始值的枚舉自動(dòng)接收一個(gè)可失敗構(gòu)造器init?(rawValue:)
,它接受一個(gè)與原始值類(lèi)型相匹配的參數(shù)rawValue
孩擂,若找到匹配的枚舉成員狼渊,則選擇;若不存在匹配值类垦,則觸發(fā)初始化失敗狈邑。
如下例所示:
enum TemperatureUnit: Character {
case kelvin = "K", celsius = "C", fahrenheit = "F"
}
let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
print("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 {
print("This is not a defined temperature unit, so initialization failed.")
}
// Prints "This is not a defined temperature unit, so initialization failed."
(3).初始化失敗的傳播
類(lèi),結(jié)構(gòu)體或枚舉的可失敗構(gòu)造器可以將其委托給同一類(lèi)蚤认,結(jié)構(gòu)體或枚舉的另一個(gè)可失敗構(gòu)造器米苹。類(lèi)似地,子類(lèi)的可失敗構(gòu)造器可以委托給超類(lèi)的可失敗構(gòu)造器烙懦。
在任何一種情況下驱入,若委托給另一個(gè)導(dǎo)致初始化失敗的構(gòu)造器,整個(gè)初始化過(guò)程立即失敗氯析,并且不會(huì)進(jìn)一步執(zhí)行初始化代碼亏较。
注意:
一個(gè)可失敗構(gòu)造器也可以委托給一個(gè)不可失敗的構(gòu)造器。如果需要向現(xiàn)有的初始化過(guò)程添加一個(gè)潛在的失敗狀態(tài)掩缓,而該初始化過(guò)程在其他情況下不會(huì)失敗雪情,請(qǐng)使用此方法。
如下例所示:
class Product {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
class CartItem: Product {
let quantity: Int
init?(name: String, quantity: Int) {
if quantity < 1 { return nil }
self.quantity = quantity
super.init(name: name)
}
}
如果創(chuàng)建CartItem
實(shí)例時(shí)你辣,使用非空的name
和大于1的quantity
,則初始化成功巡通。
if let twoSocks = CartItem(name: "sock", quantity: 2) {
print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
// Prints "Item: sock, quantity: 2"
如果創(chuàng)建CartItem
實(shí)例時(shí)尘执,傳入的quantity
等于0,則初始化失敗。
if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
print("Unable to initialize zero shirts")
}
// Prints "Unable to initialize zero shirts"
類(lèi)似的宴凉,如果創(chuàng)建CartItem
實(shí)例時(shí)誊锭,使用的name
為空字符串,則初始化失敗弥锄。
if let oneUnnamed = CartItem(name: "", quantity: 1) {
print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
print("Unable to initialize one unnamed product")
}
// Prints "Unable to initialize one unnamed product"
(4).重寫(xiě)可失敗構(gòu)造器
可以在子類(lèi)中覆蓋超類(lèi)可失敗構(gòu)造器丧靡,就像任何其他構(gòu)造器一樣∽严荆或者温治,使用子類(lèi)不可失敗構(gòu)造器覆蓋超類(lèi)可失敗構(gòu)造器。這使你能夠定義一個(gè)初始化不能失敗的子類(lèi)戒悠,即使超類(lèi)的初始化允許失敗熬荆。
注意,如果使用不可失敗的子類(lèi)構(gòu)造器覆蓋可失敗的超類(lèi)構(gòu)造器绸狐,委托給超類(lèi)構(gòu)造器的唯一方式是強(qiáng)制解包可失敗超類(lèi)構(gòu)造器的結(jié)果卤恳。
注意:
可以使用不可失敗構(gòu)造器覆蓋可失敗構(gòu)造器,但是不能反過(guò)來(lái)六孵。
如下例所示:
下面的示例定義了一個(gè)Document
類(lèi)纬黎。這個(gè)類(lèi)建模一個(gè)可以用name
屬性初始化的文檔,該屬性可以是非空字符串值劫窒,也可以是nil本今,但不能是空字符串:
class Document {
var name: String?
// this initializer creates a document with a nil name value
init() {}
// this initializer creates a document with a nonempty name value
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
下例是Document
的子類(lèi)AutomaticallyNamedDocument
。這個(gè)子類(lèi)重寫(xiě)超類(lèi)的兩個(gè)指定構(gòu)造器主巍。如果該子類(lèi)的實(shí)例初始化時(shí)沒(méi)有name
參數(shù)冠息,或者傳入一個(gè)空字符串到init(name:)
構(gòu)造器時(shí),該子類(lèi)中的兩個(gè)構(gòu)造器都能夠確保它的屬性name
有一個(gè)初始值"[Untitled]"
孕索。
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
子類(lèi)使用不可失敗構(gòu)造器init(name:)
覆蓋了父類(lèi)的可失敗構(gòu)造器init?(name:)
飘千。因?yàn)樵撟宇?lèi)用一種不同于超類(lèi)的方式處理了空字符串的情況窖逗,它的構(gòu)造器不會(huì)失敗显熏,因此提供類(lèi)不可失敗的構(gòu)造器喷兼。
可以在構(gòu)造器中使用強(qiáng)制解包來(lái)調(diào)用超類(lèi)的可失敗構(gòu)造器,作為子類(lèi)不可失敗構(gòu)造器實(shí)現(xiàn)的一部分肄渗。如下例镇眷,UntitledDocument
子類(lèi)在初始化時(shí),調(diào)用了超類(lèi)的可失敗構(gòu)造器init(name:)
翎嫡。
class UntitledDocument: Document {
override init() {
super.init(name: "[Untitled]")!
}
}
在這種情況下欠动,如果超類(lèi)的init(name:)
構(gòu)造器在調(diào)用時(shí),傳入空字符串,則這個(gè)強(qiáng)制解包操作會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤具伍。但是在子類(lèi)UntitledDocument
的構(gòu)造器中翅雏,調(diào)用它時(shí)傳入了一個(gè)字符串常量,該構(gòu)造器不會(huì)失敗人芽,因此在運(yùn)行時(shí)不會(huì)導(dǎo)致錯(cuò)誤發(fā)生望几。
(5). 可失敗構(gòu)造器init!
我們通常會(huì)定義一個(gè)可失敗構(gòu)造器,該構(gòu)造器通過(guò)在init
關(guān)鍵字(init?
)后放置問(wèn)號(hào)來(lái)創(chuàng)建適當(dāng)類(lèi)型的可選實(shí)例啼肩¢献保或者衙伶,我們也可以定義一個(gè)可失敗構(gòu)造器祈坠,該構(gòu)造器創(chuàng)建一個(gè)隱式解包的可選實(shí)例,該實(shí)例具有適當(dāng)?shù)念?lèi)型矢劲。為此赦拘,在init
關(guān)鍵字(init!
)后面放置一個(gè)感嘆號(hào),而不是問(wèn)號(hào)芬沉。
你可以從init?
委托給init!
躺同,反之亦然;也可以使用init!
覆蓋init?
丸逸,反之亦然蹋艺。 你也可以從init
委托給init !
,但是這么做黄刚,如果init!
構(gòu)造器導(dǎo)致初始化失敗捎谨,會(huì)觸發(fā)斷言操作。
7. Required構(gòu)造器
在類(lèi)構(gòu)造器的定義之前添加required
修飾符憔维,來(lái)指示類(lèi)的每個(gè)子類(lèi)都必須實(shí)現(xiàn)該初始化器涛救。
class SomeClass {
required init() {
// initializer implementation goes here
}
}
還要將required
修飾符添加到所需構(gòu)造器的每個(gè)子類(lèi)實(shí)現(xiàn)之前,以表明初始化器需求適用于繼承鏈中的其他子類(lèi)业扒。當(dāng)重寫(xiě)一個(gè)必須實(shí)現(xiàn)的指定構(gòu)造器時(shí)检吆,不用再添加override
修飾符。
class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
注意:
如果可以使用繼承的構(gòu)造器滿足需求程储,則無(wú)需顯式實(shí)現(xiàn)required
構(gòu)造器蹭沛。
8.使用閉包或函數(shù)設(shè)置默認(rèn)屬性值
如果存儲(chǔ)屬性的默認(rèn)值需要某些自定義或設(shè)置,則可以使用閉包或全局函數(shù)為該屬性提供自定義的默認(rèn)值章鲤。每當(dāng)初始化屬性所屬類(lèi)型的新實(shí)例時(shí)摊灭,將調(diào)用閉包或函數(shù),并將其返回值作為屬性的默認(rèn)值咏窿。
這些類(lèi)型的閉包或函數(shù)通常會(huì)創(chuàng)建與屬性相同類(lèi)型的臨時(shí)值斟或,調(diào)整該值以表示所需的初始狀態(tài),然后返回該臨時(shí)值作為屬性的默認(rèn)值集嵌。
使用閉包來(lái)提供默認(rèn)屬性值:
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
}()
}
注意萝挤,閉包的結(jié)束花括號(hào)后面跟著一對(duì)空的括號(hào)御毅。這告訴Swift立即執(zhí)行閉包。如果省略這些括號(hào)怜珍,則試圖將閉包本身賦值給屬性端蛆,而不是閉包的返回值。
struct Chessboard {
let boardColors: [Bool] = {
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...8 {
for j in 1...8 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
return temporaryBoard
}()
func squareIsBlackAt(row: Int, column: Int) -> Bool {
return boardColors[(row * 8) + column]
}
}
let board = Chessboard()
print(board.squareIsBlackAt(row: 0, column: 1))
// Prints "true"
print(board.squareIsBlackAt(row: 7, column: 7))
// Prints "false"
注意:
如果使用閉包初始化屬性酥泛,請(qǐng)記住今豆,在執(zhí)行閉包時(shí),實(shí)例的其余部分尚未初始化柔袁。這意味著你不能在閉包中訪問(wèn)任何其他屬性值呆躲,即使這些屬性具有默認(rèn)值。也不能使用隱式self屬性捶索,或調(diào)用實(shí)例的任何方法插掂。