Swift:15 初始化
初始化是為了使用某個類、結構體和枚舉的實例的準備過程。該過程包含為實例中的每個存儲屬性設置初始值,以及在新實例被使用之前的其他設置和初始工作。通過定義構造器來實現初始化過程,構造器是新建特定類型新實例的特殊方法。
設置存儲屬性的初始值
在創建實例時,類和結構體必須為所有存儲屬性設置合適的初始值。存儲屬性不能處于未設置值的狀態。
調用構造器可以創建一個特定類型的新實例。最簡單的構造器和不帶形參的實例方法一樣,使用init關鍵詞。如下實例所示。
struct Point{
var x:Int,y:Int
init() {
x = 0
y = 0
}
}
正如上面的示例,可以在構造器中為存儲屬性設置初始值。此外,還可以在聲明屬性時為其設置默認屬性值。設置默認屬性值的方式是在屬性定義是為其賦初始值。
struct Point{
var x:Int=0,y:Int=0
}
自定義初始化
構造器init方法與實例方法一樣,也可以在構造器中設置外部使用的形參名和實參名,如果外部形參名省略,默認實參名與形參名同名。當然,也可以在參數名前加下劃線(_)來表示方法調用時省略形參名。一個類中可以有多個構造器,構造器之間是通過方法參數個數和類型進行區分的。如下示例演示構造器參數的使用。
class Color{
var red:Int,green:Int,blue=0
init(_ red:Int) {
self.red = red
self.green = 0
}
init(red:Int,withGreen green:Int) {
self.red = red
self.green = green
}
init(red:Int,green:Int,blue:Int) {
self.red = red
self.green = green
self.blue = blue
}
func toString() {
print("(r:\(self.red),g:\(self.green),b:\(self.blue))")
}
}
let color1 = Color(10)
color1.toString() //打印結果:(r:10,g:0,b:0)
let color2 = Color(red:10,withGreen:20)
color2.toString() //打印結果:(r:10,g:20,b:0)
let color3 = Color(red: 10, green: 20, blue: 30)
color3.toString() //打印結果:(r:10,g:20,b:30)
如果自定義的類型包含一個邏輯上允許可以為空值的存儲屬性。不管是因為它無法在初始化時設置它的值,還是因為它可以在之后可以設置為空,都需要將這種屬性聲明為可選(optional)類型??蛇x類型的屬性將自動初始化為nil,表示特意讓這個屬性在初始化過程中沒有值。
class Robot{
var question:String
var anster:String?
init(question:String) {
self.question = question
}
func think() {
if question == "今天多少號"{
anster = "2017-2-8"
}else{
anster = "無法明白你的意思"
}
}
}
let robot = Robot(question: "今天多少號")
robot.think()
print("\(robot.anster)")
代碼執行結果截圖如下所示。
默認構造器
當類或者結構體中所有的屬性都有默認值,并且沒有類中沒有自定義構造器時,Swift將為他們提供一個默認構造器。如下示例。
class Color{
var red:Int=255,green:Int=255,blue=0
}
let color = Color()
如果結構體對所有存儲屬性提供了默認值并且沒有提供自定義的構造器,該結構體將會自動獲得一個成員構造器。如下示例所示。
struct Point{
var x:Int,y:Int
}
let point = Point(x: 10, y: 20)
值類型的構造器代理
構造器可以通過調用其它構造器來完成實例的部分初始化工作。這一過程被稱作構造器代理。
對于值類型,使用self.init來引用相同值類型中的其它構造器來打造自定義的構造器。你只能在構造器內部調用self.init。如果值類型自定義了構造器,該類型的默認構造器就無法再被訪問。值類型構造器代理使用示例如下。
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)
}
func toString() {
print("[origin:(\(origin.x),\(origin.y)),size:(\(size.width),\(size.height))]")
}
}
let r1 = Rect()
r1.toString()
let r2 = Rect(origin: Point(x:10,y:10), size: Size(width: 6.0, height: 6.0))
r2.toString()
let r3 = Rect(center: Point(x:13,y:13), size: Size(width: 6.0, height: 6.0))
r3.toString()
代碼執行結果如下圖。
類的繼承和初始化
所有類的存儲屬性,包括所有從超類繼承的屬性,都必須在初始化的過程中賦初始值。Swift定義了兩種構造器來保證所有存儲屬性獲得初始值。分別為指定構造器(designated initializer)和便捷構造器(convenience initializers)。
指定構造器是類的最主要的構造器。指定構造器將類引入的所有屬性初始化,并且可以沿著超類鏈來調用超類的構造函數。每個類都必須至少有一個指定構造器,一般就只有一個。
便捷構造器起到的是輔助的作用。你可以使用便捷構造器調用同類的指定構造器(其中指定構造器的形參被設置為默認值)。還可以針對特定的用途和輸入值類型來定義便捷構造器完成生成新實例的工作。在不需要的情況下沒必要提供便捷構造器。便捷構造器是一種幫助開發者節省時間和讓初始化更加清晰的快速途徑。
為了簡化指定構造器和便捷構造器的關系。Swift運用了如下三條規則來實現構造器之間的代理調用:
- 指定構造器必須調用直接超類(如果有超類)的指定構造器。
- 便捷構造器只能調用同一個類中的其他構造器。
- 便捷構造器必須以調用指定構造器作為結束。
可以簡單的理解為:指定構造器必須向上代理,便捷構造器必須橫向代理。
這些規則也可以通過下圖說明。
和Objective-C的子類不同,Swift的子類默認不繼承超類的構造器。這種行為防止了特殊的子類自動繼承簡單父類的構造器,并且用這個構造器來創建一個沒有完全或正確初始化的子類的新實例。
但是在某些條件滿足的情況下,超類構造器是會被自動繼承的。在實踐中,這意味著你在絕大多數場景中不需要進行構造器覆蓋,在任何安全的時候,用最小的代價來繼承超類構造器。
假設你為所有子類引入的新屬性都提供了默認值,會運用如下兩條規則:
- 如果子類沒有定義指定構造器,自動繼承超類的所有指定構造器。
- 如果子類提供了超類所有指定構造器的實現,則它會自動繼承超類的所有便捷構造器。
指定構造器的語法和值類型的構造器的語法相同,就是init方法。而便捷構造器是在init之前使用關鍵詞convenience。下面使用一個示例來演示和實踐指定構造器和便捷構造器用法。
class Vehicle{
var name:String
init(name:String) {
self.name = name
}
convenience init() {
self.init(name:"匿名")
}
}
class Bike:Vehicle{
var wheel:Int
init(name:String,wheel:Int) {
self.wheel = wheel
super.init(name: name)
}
override convenience init(name:String){
self.init(name:name,wheel:2)
}
}
class Car:Bike{
var carNo:String = "88888"
var description: String {
let output = "Name:\(name)\t Wheel:\(wheel)\t No:\(carNo)"
return output
}
}
let car1 = Car()
print(car1.description)
let car2 = Car(name: "小白")
print(car2.description)
var car3 = Car(name: "小白", wheel: 4)
car3.carNo = "R8V23"
print(car3.description)
上面示例中,Vehicle定義了一個指定構造器init(name:String)和一個便捷構造器init(),Bike中定義了一個指定構造器init(name:String,wheel:Int)調用了Vehicle中的指定構造器,還覆蓋定義了便捷構造器init(name:String),同時自動繼承了Vehicle的便捷構造器init()。Car自動繼承了Bike中的所有構造器。
代碼執行結果如下圖所示。
示例代碼
https://github.com/99ios/23.16