- swift中的
initialization
方法實現中不使用return
不返回對象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"
- 通過
initializer
創建初始值和在屬性定義時賦予默認值,不會調用property observer
- 由于
init
方法的方法名必須為init
且不能重定義,所以括號內的argument label
特別重要。并且如果你沒有寫,系統會為你提供。如果你不需要,也可以使用_去掉(和其他函數方法參數結構一樣) - 類中的屬性必須有
default value
,在屬性定義和構造器中賦值,如果不賦予初始化值要標記成可選類型。不賦值的可選類型默認為空,可以被理解成還沒有值,可以不在構造器中賦值。 - 常量屬性必須在定義時賦值,或者在構造器中賦值。對于類實例來說,只能在定義這個常量屬性的類中使用構造器賦值,不能再子類的構造器中賦值。
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.)"
- 當屬性都有默認值時,且沒有自定義構造器時,swift 會提供默認構造器。
class ShoppingListItem { var name: String? var quantity = 1 var purchased = false } var item = ShoppingListItem()
- 對于結構體而言,當沒有自定義構造器的時候,會自動生成逐一成員構造器。不同于上面的默認構造器,逐一成員構造器允許屬性在定義的時候沒有默認值。如果有自定義構造器,則不會生成默認構造器和逐一成員構造器,這是為了防止錯用。如果想在自定義構造器的同時也生成默認,使用擴展(extension)
struct Size { var width = 0.0, height = 0.0 } let twoByTwo = Size(width: 2.0, height: 2.0)
- 可以自定義多個構造器,在復雜構造器中使用self.init調用其他簡單的自定義構造器
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 originRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0)) // originRect's origin is (2.0, 2.0) and its size is (5.0, 5.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)
- 指定構造器和普通構造器格式一樣,至少要有一個;便利構造器在前面加上convenience關鍵字,可有可無。通常用必不可少的指定構造器來創建實例,而使用便利構造器來個性化定制子類。例如需要多個參數的指定構造器,可以創建需要一個參數的便利構造器,在實現中調用指定構造器,其他參數傳默認值,做到傳入更少參數便利創建實例,如下
init(name: String) { self.name = name } convenience init() { self.init(name: "[Unnamed]]") }
- 關于構造器原則:1.指定構造器必須調用其父類指定構造器;2.便利構造器必須調用類中其他構造器;3.便利構造器必須最終調用指定構造器。簡單說:指定構造器向上代理,便利構造器橫向代理
- swift中構造器的兩個階段,第一個階段賦予所有變量默認值,第二個階段變量在使用前靈活處理
- 構造器的安全檢查:1.必須指定所有存儲變量后才可以向上調用父類構造器;2.指定構造器必須在繼承屬性賦值前向上調用父類構造器,否則賦值會被覆蓋;3.便利構造器必須先調用其他構造器,后賦值,原理同上;4.第一階段結束前不允許調用和讀取任何實例屬性及實例方法
- 第一階段:類調用指定或便利構造器——為新實例分配內存,沒有被完全初始化—— 指定構造器為所有存儲屬性賦初始值,所有存儲屬性擁有內存——指定構造器調用其父類構造器執行同樣操作——反復調用繼承樹直到頂端——直到頂端所有屬性存儲屬性有值之后,新實例內存完全分配完成,第一階段結束
- 第二階段:從上往下,依次給每個指定構造器機會定制實例,更改屬性并調用實例方法,直到低端
- 指定構造器都在第一階段,在便利構造器調用完指定構造器后可以修改變量,這部分是第二階段,如下
class Student { var name = "xiaoming" var old: String init() { // print("\(self.name)") 第一階段未結束,實例未初始化完成,讀取self會導致error self.old = "16" //寫入賦值可以 在指定構造器中設置默認值 } convenience init(old: String) { self.init() //至此,第一階段結束 print("\(self.name)") //進入第二階段,可以讀取self,此后為靈活處理屬性的地方 //在便利構造器中定義個性值 self.old = old } }
- swift中子類不繼承父類的構造方法
- 關于構造器繼承的兩個規則
1.如果子類沒有定義任何指定構造器,則自動繼承父類所有指定構造器
2.如果子類實現了所有父類中的指定構造器,無論是自己定義還是由1繼承的,那么子類會繼承所有父類的便利構造器 - 使用override重載父類構造方法,當子類存在和父類方法名相同的方法時必須要有Override,否則會報錯
class Bicyle: Vehicle { override init() { super.init() //至此第一階段結束,子類有機會重寫初始化值 self.numberOfWheels = 2 } }
- 使用?表示可失敗構造器,防止必要參數缺失時錯誤的成功創建實例
- 嚴格說構造方法不返回實例,只是確保self在使用的時候被初始化完成,所以對于可失敗構造器來說,失敗返回nil,成功不用返回
struct Animal { let species: String init?(species: String){ if species.isEmpty { return nil } self.species = species } }
- 枚舉為原值自動提供可失敗初始化方式
- 使用可失敗構造器調用自身或父類的另一個可失敗構造器,可以使用新的可失敗構造器在原可失敗構造器上添加額外的失敗情況
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) } }
- 可使用不可失敗構造器調用失敗構造器,在其中給予失敗條件固定值,并使用!對于確定不會失敗的可失敗構造器進行強制拆包
class Document { var name: String? init() { } init?(name: String) { if name.isEmpty { return nil } self.name = name } } 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 } } } class UntitledDocument: Document { override init() { super.init(name: "[Untitled]")! } }
- 使用required關鍵字來表示必備構造器,子類也必須實現,不需要override關鍵字而是繼續使用required
- 使用閉包給函數屬性賦予初始值,注意閉包內類本身沒有構造完成,所以不能在閉包函數內調用其他類屬性(即使屬性有默認值),不能在閉包內使用self,不能在閉包內調用其他類中函數。實際上就是給某些相對復雜的屬性使用一個新的匿名函數賦值,而由于新的匿名函數沒有參數,所以不能使用匿名函數外的變量。
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 squareIsBlckAt(row: Int, column: Int) -> Bool { return boardColors[(row * 8) + column] } }
代碼示例
函數繼承樹關于構造器的繼承以及指定構造器和便利構造器的應用。
class Food {
var name: String
init(name: String) {
self.name = name
}
//提供一個帶有默認值的便利構造器
convenience init() {
self.init(name: "[Unnamed]]")
}
}
let namedMeat = Food.init(name: "Bacon")
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
//本類中的屬性可以在super.init前面賦值,而父類的屬性要在super.init后賦值
self.quantity = quantity
super.init(name: name)
}
//提供一個便利構造器不指定個數時默認為1
//此方法實際上重載的是food中的指定構造器
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
//和父類中的便利構造器相同,但是不需要override重載
convenience init() {
self.init(name: "default", quantity: 0)
}
}
let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient.init(name: "Bacon")
let sixEggs = RecipeIngredient.init(name: "Eggs", quantity: 6)
class AShoppingListItem: RecipeIngredient {
var purchased = false
// 使用閉包返回一個只讀計算屬性
var description: String {
var output = "\(quantity) x \(name) "
output += purchased ? "??" : "?"
return output
}
}
var breakfaseList = [AShoppingListItem(), AShoppingListItem.init(name: "Bacon"), AShoppingListItem.init(name: "Eggs", quantity: 6)]
breakfaseList[0].name = "Orange juice"
breakfaseList[0].purchased = true
for item in breakfaseList {
print("\(item.description)")
}
補充幾點
- 方法名,參數個數,參數名完全相同的方法才算是一個方法,才需要用Override重載
- 使用convenience定義的構造器一定要調用指定構造器,否則會報錯
- 如果子類中定義的convenience構造器有和父類中的某個指定構造器方法重疊,也需要使用override重載
- 如果子類中定義的convenience構造器于父類中某個convenience方法相同,不需要使用override重載