Swift值類型的構造器代理
值類型的構造器代理
構造器可以通過調用其它構造器來完成實例的部分構造過程。這一過程稱爲構造器代理,它能減少多個構造器間的代碼重複。
構造器代理的實現規則和形式在值類型和類類型中有所不同。值類型(結構體和枚舉類型)不支持繼承,所以構造器代理的過程相對簡單,因爲它們只能代理任務給本身提供的其它構造器。類則不同,它可以繼承自其它類(請參考繼承),這意味着類有責任保證其所有繼承的存儲型屬性在構造時也能正確的初始化。這些責任將在後續章節類的繼承和構造過程中介紹。
對於值類型,你可以使用self.init
在自定義的構造器中引用其它的屬於相同值類型的構造器。並且你只能在構造器內部調用self.init
。
注意,如果你爲某個值類型定義了一個定製的構造器,你將無法訪問到默認構造器(如果是結構體,則無法訪問逐一對象構造器)。這個限制可以防止你在爲值類型定義了一個更復雜的,完成了重要準備構造器之後,別人還是錯誤的使用了那個自動生成的構造器。
注意:
假如你想通過默認構造器、逐一對象構造器以及你自己定製的構造器爲值類型創建實例,我們建議你將自己定製的構造器寫到擴展(extension
)中,而不是跟值類型定義混在一起。想查看更多內容,請查看擴展章節。
下面例子將定義一個結構體Rect
,用來展現幾何矩形。這個例子需要兩個輔助的結構體Size
和Point
,它們各自爲其所有的屬性提供了初始值0.0
。
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
你可以通過以下三種方式爲Rect
創建實例--使用默認的0值來初始化origin
和size
屬性;使用特定的origin
和size
實例來初始化;使用特定的center
和size
來初始化。在下面Rect
結構體定義中,我們爲着三種方式提供了三個自定義的構造器:
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)
}
}
第一個Rect
構造器init()
,在功能上跟沒有自定義構造器時自動獲得的默認構造器是一樣的。這個構造器是一個空函數,使用一對大括號{}
來描述,它沒有執行任何定製的構造過程。調用這個構造器將返回一個Rect
實例,它的origin
和size
屬性都使用定義時的默認值Point(x: 0.0, y: 0.0)
和Size(width: 0.0, height: 0.0)
:
let basicRect = Rect()
// basicRect 的原點是 (0.0, 0.0),尺寸是 (0.0, 0.0)
第二個Rect
構造器init(origin:size:)
,在功能上跟結構體在沒有自定義構造器時獲得的逐一成員構造器是一樣的。這個構造器只是簡單的將origin
和size
的參數值賦給對應的存儲型屬性:
let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
// originRect 的原點是 (2.0, 2.0),尺寸是 (5.0, 5.0)
第三個Rect
構造器init(center:size:)
稍微複雜一點。它先通過center
和size
的值計算出origin
的座標。然後再調用(或代理給)init(origin:size:)
構造器來將新的origin
和size
值賦值到對應的屬性中:
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
// centerRect 的原點是 (2.5, 2.5),尺寸是 (3.0, 3.0)
構造器init(center:size:)
可以自己將origin
和size
的新值賦值到對應的屬性中。然而儘量利用現有的構造器和它所提供的功能來實現init(center:size:)
的功能,是更方便、更清晰和更直觀的方法。
注意:
如果你想用另外一種不需要自己定義init()
和init(origin:size:)
的方式來實現這個例子,請參考擴展。