> 1.0 翻譯:[lifedim](https://github.com/lifedim)?校對:[lifedim](https://github.com/lifedim)
>
> 2.0 翻譯+校對:[chenmingbiao](https://github.com/chenmingbiao)
本頁包含內容:
[TOC=2]
構造過程是為了使用某個類、結構體或枚舉類型的實例而進行的準備過程。這個過程包含了為實例中的每個存儲型屬性設置初始值和為其執行必要的準備和初始化任務。
構造過程是通過定義構造器(`Initializers`)來實現的,這些構造器可以看做是用來創建特定類型實例的特殊方法。與 Objective-C 中的構造器不同,Swift 的構造器無需返回值,它們的主要任務是保證新實例在第一次使用前完成正確的初始化。
類的實例也可以通過定義析構器(`deinitializer`)在實例釋放之前執行特定的清除工作。想了解更多關于析構器的內容,請參考[析構過程](http://wiki.jikexueyuan.com/project/swift/chapter2/15_Deinitialization.html)。
## 存儲型屬性的初始賦值
類和結構體在實例創建時,必須為所有存儲型屬性設置合適的初始值。存儲型屬性的值不能處于一個未知的狀態。
你可以在構造器中為存儲型屬性賦初值,也可以在定義屬性時為其設置默認值。以下章節將詳細介紹這兩種方法。
> 注意:
> 當你為存儲型屬性設置默認值或者在構造器中為其賦值時,它們的值是被直接設置的,不會觸發任何屬性觀測器(`property observers`)。
### 構造器
構造器在創建某特定類型的新實例時調用。它的最簡形式類似于一個不帶任何參數的實例方法,以關鍵字`init`命名。
~~~
init() {
// 在此處執行構造過程
}
~~~
下面例子中定義了一個用來保存華氏溫度的結構體`Fahrenheit`,它擁有一個`Double`類型的存儲型屬性`temperature`:
~~~
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit")
// 輸出 "The default temperature is 32.0° Fahrenheit”
~~~
這個結構體定義了一個不帶參數的構造器`init`,并在里面將存儲型屬性`temperature`的值初始化為`32.0`(華攝氏度下水的冰點)。
### 默認屬性值
如前所述,你可以在構造器中為存儲型屬性設置初始值。同樣,你也可以在屬性聲明時為其設置默認值。
> 注意:
> 如果一個屬性總是使用同一個初始值,可以為其設置一個默認值。無論定義默認值還是在構造器中賦值,最終它們實現的效果是一樣的,只不過默認值將屬性的初始化和屬性的聲明結合的更緊密。使用默認值能讓你的構造器更簡潔、更清晰,且能通過默認值自動推導出屬性的類型;同時,它也能讓你充分利用默認構造器、構造器繼承(后續章節將講到)等特性。
你可以使用更簡單的方式在定義結構體`Fahrenheit`時為屬性`temperature`設置默認值:
~~~
struct Fahrenheit {
var temperature = 32.0
}
~~~
## 自定義構造過程
你可以通過輸入參數和可選屬性類型來定義構造過程,也可以在構造過程中修改常量屬性。這些都將在后面章節中提到。
### 構造參數
你可以在定義構造器時提供構造參數,為其提供自定義構造所需值的類型和名字。構造器參數的功能和語法跟函數和方法參數相同。
下面例子中定義了一個包含攝氏度溫度的結構體`Celsius`。它定義了兩個不同的構造器:`init(fromFahrenheit:)`和`init(fromKelvin:)`,二者分別通過接受不同刻度表示的溫度值來創建新的實例:
~~~
struct Celsius {
var temperatureInCelsius: Double = 0.0
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 是 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius 是 0.0”
~~~
第一個構造器擁有一個構造參數,其外部名字為`fromFahrenheit`,內部名字為`fahrenheit`;第二個構造器也擁有一個構造參數,其外部名字為`fromKelvin`,內部名字為`kelvin`。這兩個構造器都將唯一的參數值轉換成攝氏溫度值,并保存在屬性`temperatureInCelsius`中。
### 參數的內部名稱和外部名稱
跟函數和方法參數相同,構造參數也存在一個在構造器內部使用的參數名字和一個在調用構造器時使用的外部參數名字。
然而,構造器并不像函數和方法那樣在括號前有一個可辨別的名字。所以在調用構造器時,主要通過構造器中的參數名和類型來確定需要調用的構造器。正因為參數如此重要,如果你在定義構造器時沒有提供參數的外部名字,Swift 會為每個構造器的參數自動生成一個跟內部名字相同的外部名,就相當于在每個構造參數之前加了一個哈希符號。
以下例子中定義了一個結構體`Color`,它包含了三個常量:`red`、`green`和`blue`。這些屬性可以存儲0.0到1.0之間的值,用來指示顏色中紅、綠、藍成分的含量。
`Color`提供了一個構造器,其中包含三個`Double`類型的構造參數。`Color`也可以提供第二個構造器,它只包含`Double`類型名叫`white`的參數,它被用于給上述三個構造參數賦予同樣的值。
~~~
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
}
}
~~~
兩種構造器都能用于創建一個新的`Color`實例,你需要為構造器每個外部參數傳值。
~~~
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)
// 報編譯時錯誤,需要外部名稱
~~~
### 不帶外部名的構造器參數
如果你不希望為構造器的某個參數提供外部名字,你可以使用下劃線(_)來顯示描述它的外部名,以此重寫上面所說的默認行為。
下面是之前`Celsius`例子的擴展,跟之前相比添加了一個帶有`Double`類型參數名為`celsius`的構造器,其外部名用`_`代替。
~~~
struct Celsius {I
var temperatureInCelsius: Double = 0.0
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 為 37.0
~~~
調用這種不需要外部參數名稱的`Celsius(37.0)`構造器看起來十分簡明的。因此適當使用這種`init(_ celsius: Double)`構造器可以提供`Double`類型的參數值而不需要加上外部名。
### 可選屬性類型
如果你定制的類型包含一個邏輯上允許取值為空的存儲型屬性--不管是因為它無法在初始化時賦值,還是因為它可以在之后某個時間點可以賦值為空--你都需要將它定義為可選類型`optional type`。可選類型的屬性將自動初始化為空`nil`,表示這個屬性是故意在初始化時設置為空的。
下面例子中定義了類`SurveyQuestion`,它包含一個可選字符串屬性`response`:
~~~
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()
// 輸出 "Do you like cheese?"
cheeseQuestion.response = "Yes, I do like cheese."
~~~
調查問題在問題提出之后,我們才能得到回答。所以我們將屬性回答`response`聲明為`String?`類型,或者說是可選字符串類型`optional String`。當`SurveyQuestion`實例化時,它將自動賦值為空`nil`,表明暫時還不存在此字符串。
### 構造過程中常量屬性的修改
只要在構造過程結束前常量的值能確定,你可以在構造過程中的任意時間點修改常量屬性的值。
> 注意:
> 對某個類實例來說,它的常量屬性只能在定義它的類的構造過程中修改;不能在子類中修改。
你可以修改上面的`SurveyQuestion`示例,用常量屬性替代變量屬性`text`,指明問題內容`text`在其創建之后不會再被修改。盡管`text`屬性現在是常量,我們仍然可以在其類的構造器中設置它的值:
~~~
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()
// 輸出 "How about beets?"
beetsQuestion.response = "I also like beets. (But not with cheese.)"
~~~
## 默認構造器
Swift 將為所有屬性已提供默認值的且自身沒有定義任何構造器的結構體或基類,提供一個默認的構造器。這個默認構造器將簡單的創建一個所有屬性值都設置為默認值的實例。
下面例子中創建了一個類`ShoppingListItem`,它封裝了購物清單中的某一項的屬性:名字(`name`)、數量(`quantity`)和購買狀態?`purchase state`。
~~~
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
~~~
由于`ShoppingListItem`類中的所有屬性都有默認值,且它是沒有父類的基類,它將自動獲得一個可以為所有屬性設置默認值的默認構造器(盡管代碼中沒有顯式為`name`屬性設置默認值,但由于`name`是可選字符串類型,它將默認設置為`nil`)。上面例子中使用默認構造器創造了一個`ShoppingListItem`類的實例(使用`ShoppingListItem()`形式的構造器語法),并將其賦值給變量`item`。
### 結構體的逐一成員構造器
除上面提到的默認構造器,如果結構體對所有存儲型屬性提供了默認值且自身沒有提供定制的構造器,它們能自動獲得一個逐一成員構造器。
逐一成員構造器是用來初始化結構體新實例里成員屬性的快捷方法。我們在調用逐一成員構造器時,通過與成員屬性名相同的參數名進行傳值來完成對成員屬性的初始賦值。
下面例子中定義了一個結構體`Size`,它包含兩個屬性`width`和`height`。Swift 可以根據這兩個屬性的初始賦值`0.0`自動推導出它們的類型`Double`。
由于這兩個存儲型屬性都有默認值,結構體`Size`自動獲得了一個逐一成員構造器?`init(width:height:)`。 你可以用它來為`Size`創建新的實例:
~~~
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
~~~
## 值類型的構造器代理
構造器可以通過調用其它構造器來完成實例的部分構造過程。這一過程稱為構造器代理,它能減少多個構造器間的代碼重復。
構造器代理的實現規則和形式在值類型和類類型中有所不同。值類型(結構體和枚舉類型)不支持繼承,所以構造器代理的過程相對簡單,因為它們只能代理給本身提供的其它構造器。類則不同,它可以繼承自其它類(請參考[繼承](http://wiki.jikexueyuan.com/project/swift/chapter2/13_Inheritance.html)),這意味著類有責任保證其所有繼承的存儲型屬性在構造時也能正確的初始化。這些責任將在后續章節[類的繼承和構造過程](http://wiki.jikexueyuan.com/project/swift/chapter2/14_Initialization.html#class_inheritance_and_initialization)中介紹。
對于值類型,你可以使用`self.init`在自定義的構造器中引用其它的屬于相同值類型的構造器。并且你只能在構造器內部調用`self.init`。
如果你為某個值類型定義了一個定制的構造器,你將無法訪問到默認構造器(如果是結構體,則無法訪問逐一對象構造器)。這個限制可以防止你在為值類型定義了一個更復雜的,完成了重要準備構造器之后,別人還是錯誤的使用了那個自動生成的構造器。
> 注意:
> 假如你想通過默認構造器、逐一對象構造器以及你自己定制的構造器為值類型創建實例,我們建議你將自己定制的構造器寫到擴展(`extension`)中,而不是跟值類型定義混在一起。想查看更多內容,請查看[擴展](http://wiki.jikexueyuan.com/project/swift/chapter2/20_Extensions.html)章節。
下面例子將定義一個結構體`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:)`的方式來實現這個例子,請參考[擴展](http://wiki.jikexueyuan.com/project/swift/chapter2/20_Extensions.html)。
## 類的繼承和構造過程
類里面的所有存儲型屬性--包括所有繼承自父類的屬性--都必須在構造過程中設置初始值。
Swift 提供了兩種類型的類構造器來確保所有類實例中存儲型屬性都能獲得初始值,它們分別是指定構造器和便利構造器。
### 指定構造器和便利構造器
指定構造器是類中最主要的構造器。一個指定構造器將初始化類中提供的所有屬性,并根據父類鏈往上調用父類的構造器來實現父類的初始化。
每一個類都必須擁有至少一個指定構造器。在某些情況下,許多類通過繼承了父類中的指定構造器而滿足了這個條件。具體內容請參考后續章節[自動構造器的繼承](http://wiki.jikexueyuan.com/project/swift/chapter2/14_Initialization.html#automatic_initializer_inheritance)。
便利構造器是類中比較次要的、輔助型的構造器。你可以定義便利構造器來調用同一個類中的指定構造器,并為其參數提供默認值。你也可以定義便利構造器來創建一個特殊用途或特定輸入的實例。
你應當只在必要的時候為類提供便利構造器,比方說某種情況下通過使用便利構造器來快捷調用某個指定構造器,能夠節省更多開發時間并讓類的構造過程更清晰明了。
### 指定構造器和便利構造器的語法
類的指定構造器的寫法跟值類型簡單構造器一樣:
~~~
init(parameters) {
statements
}
~~~
便利構造器也采用相同樣式的寫法,但需要在`init`關鍵字之前放置`convenience`關鍵字,并使用空格將它們倆分開:
~~~
convenience init(parameters) {
statements
}
~~~
### 類的構造器代理規則
為了簡化指定構造器和便利構造器之間的調用關系,Swift 采用以下三條規則來限制構造器之間的代理調用:
#### 規則 1
指定構造器必須調用其直接父類的的指定構造器。
#### 規則 2
便利構造器必須調用同一類中定義的其它構造器。
#### 規則 3
便利構造器必須最終以調用一個指定構造器結束。
一個更方便記憶的方法是:
* 指定構造器必須總是向上代理
* 便利構造器必須總是橫向代理
這些規則可以通過下面圖例來說明:

如圖所示,父類中包含一個指定構造器和兩個便利構造器。其中一個便利構造器調用了另外一個便利構造器,而后者又調用了唯一的指定構造器。這滿足了上面提到的規則2和3。這個父類沒有自己的父類,所以規則1沒有用到。
子類中包含兩個指定構造器和一個便利構造器。便利構造器必須調用兩個指定構造器中的任意一個,因為它只能調用同一個類里的其他構造器。這滿足了上面提到的規則2和3。而兩個指定構造器必須調用父類中唯一的指定構造器,這滿足了規則1。
> 注意:
> 這些規則不會影響使用時,如何用類去創建實例。任何上圖中展示的構造器都可以用來完整創建對應類的實例。這些規則只在實現類的定義時有影響。
下面圖例中展示了一種針對四個類的更復雜的類層級結構。它演示了指定構造器是如何在類層級中充當“管道”的作用,在類的構造器鏈上簡化了類之間的相互關系。

### 兩段式構造過程
Swift 中類的構造過程包含兩個階段。第一個階段,每個存儲型屬性通過引入它們的類的構造器來設置初始值。當每一個存儲型屬性值被確定后,第二階段開始,它給每個類一次機會在新實例準備使用之前進一步定制它們的存儲型屬性。
兩段式構造過程的使用讓構造過程更安全,同時在整個類層級結構中給予了每個類完全的靈活性。兩段式構造過程可以防止屬性值在初始化之前被訪問;也可以防止屬性被另外一個構造器意外地賦予不同的值。
> 注意:
> Swift的兩段式構造過程跟 Objective-C 中的構造過程類似。最主要的區別在于階段 1,Objective-C 給每一個屬性賦值`0`或空值(比如說`0`或`nil`)。Swift 的構造流程則更加靈活,它允許你設置定制的初始值,并自如應對某些屬性不能以`0`或`nil`作為合法默認值的情況。
Swift 編譯器將執行 4 種有效的安全檢查,以確保兩段式構造過程能順利完成:
#### 安全檢查 1
指定構造器必須保證它所在類引入的所有屬性都必須先初始化完成,之后才能將其它構造任務向上代理給父類中的構造器。
如上所述,一個對象的內存只有在其所有存儲型屬性確定之后才能完全初始化。為了滿足這一規則,指定構造器必須保證它所在類引入的屬性在它往上代理之前先完成初始化。
#### 安全檢查 2
指定構造器必須先向上代理調用父類構造器,然后再為繼承的屬性設置新值。如果沒這么做,指定構造器賦予的新值將被父類中的構造器所覆蓋。
#### 安全檢查 3
便利構造器必須先代理調用同一類中的其它構造器,然后再為任意屬性賦新值。如果沒這么做,便利構造器賦予的新值將被同一類中其它指定構造器所覆蓋。
#### 安全檢查 4
構造器在第一階段構造完成之前,不能調用任何實例方法、不能讀取任何實例屬性的值,`self`的值不能被引用。
類實例在第一階段結束以前并不是完全有效,僅能訪問屬性和調用方法,一旦完成第一階段,該實例才會聲明為有效實例。
以下是兩段式構造過程中基于上述安全檢查的構造流程展示:
#### 階段 1
* 某個指定構造器或便利構造器被調用;
* 完成新實例內存的分配,但此時內存還沒有被初始化;
* 指定構造器確保其所在類引入的所有存儲型屬性都已賦初值。存儲型屬性所屬的內存完成初始化;
* 指定構造器將調用父類的構造器,完成父類屬性的初始化;
* 這個調用父類構造器的過程沿著構造器鏈一直往上執行,直到到達構造器鏈的最頂部;
* 當到達了構造器鏈最頂部,且已確保所有實例包含的存儲型屬性都已經賦值,這個實例的內存被認為已經完全初始化。此時階段1完成。
#### 階段 2
* 從頂部構造器鏈一直往下,每個構造器鏈中類的指定構造器都有機會進一步定制實例。構造器此時可以訪問`self`、修改它的屬性并調用實例方法等等。
* 最終,任意構造器鏈中的便利構造器可以有機會定制實例和使用`self`。
下圖展示了在假定的子類和父類之間構造的階段1:

在這個例子中,構造過程從對子類中一個便利構造器的調用開始。這個便利構造器此時沒法修改任何屬性,它把構造任務代理給同一類中的指定構造器。
如安全檢查1所示,指定構造器將確保所有子類的屬性都有值。然后它將調用父類的指定構造器,并沿著造器鏈一直往上完成父類的構建過程。
父類中的指定構造器確保所有父類的屬性都有值。由于沒有更多的父類需要構建,也就無需繼續向上做構建代理。
一旦父類中所有屬性都有了初始值,實例的內存被認為是完全初始化,而階段1也已完成。
以下展示了相同構造過程的階段2:

父類中的指定構造器現在有機會進一步來定制實例(盡管它沒有這種必要)。
一旦父類中的指定構造器完成調用,子類的構指定構造器可以執行更多的定制操作(同樣,它也沒有這種必要)。
最終,一旦子類的指定構造器完成調用,最開始被調用的便利構造器可以執行更多的定制操作。
### 構造器的繼承和重寫
跟 Objective-C 中的子類不同,Swift 中的子類不會默認繼承父類的構造器。Swift 的這種機制可以防止一個父類的簡單構造器被一個更專業的子類繼承,并被錯誤的用來創建子類的實例。
> 注意: 父類的構造器僅在確定和安全的情況下被繼承。具體內容請參考后續章節[自動構造器的繼承](http://wiki.jikexueyuan.com/project/swift/chapter2/14_Initialization.html#automatic_initializer_inheritance)。
假如你希望自定義的子類中能實現一個或多個跟父類相同的構造器,也許是為了完成一些定制的構造過程,你可以在你定制的子類中提供和重寫與父類相同的構造器。
當你寫一個父類中帶有指定構造器的子類構造器時,你需要重寫這個指定的構造器。因此,你必須在定義子類構造器時帶上`override`修飾符。即使你重寫系統提供的默認構造器也需要帶上`override`修飾符,具體內容請參考[默認構造器](http://wiki.jikexueyuan.com/project/swift/chapter2/14_Initialization.html#default_initializers)。
無論是重寫屬性,方法或者是下標腳本,只要含有`override`修飾符就會去檢查父類是否有相匹配的重寫指定構造器和驗證重寫構造器參數。
> 注意:
> 當你重寫一個父類指定構造器時,你需要寫`override`修飾符,甚至你的子類構造器繼承的是父類的便利構造器。
相反地,如果你寫了一個和父類便利構造器相匹配的子類構造器,子類都不能直接調用父類的便利構造器,每個規則都在上文[構造器鏈](http://wiki.jikexueyuan.com/project/swift/chapter2/14_Initialization.html#initialization_chain)有所描述。
在下面的例子中定義了一個基礎類叫`Vehicle`。基礎類中聲明了一個存儲型屬性`numberOfWheels`,它是值為`0`的`Int`類型屬性。`numberOfWheels`屬性用于創建名為`descrpiption`類型為`String`的計算型屬性。
~~~
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
~~~
`Vehicle`類只為存儲型屬性提供默認值,而不自定義構造器。因此,它會自動生成一個默認構造器,具體內容請參考[默認構造器](http://wiki.jikexueyuan.com/project/swift/chapter2/14_Initialization.html#default_initializers)。默認構造器通常在類中是指定構造器,它可以用于創建屬性叫`numberOfWheels`值為`0`的`Vehicle`實例。
~~~
let vehicle = Vehicle()
print("Vehicle: \(vehicle.description)")
// Vehicle: 0 wheel(s)
~~~
下面例子中定義了一個`Vehicle`的子類`Bicycle`:
~~~
class Bicycle: Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}
~~~
子類`Bicycle`定義了一個自定義指定構造器`init()`。這個指定構造器和父類的指定構造器相匹配,所以`Bicycle`中的指定構造器需要帶上`override`修飾符。
`Bicycle`的構造器`init()`一開始調用`super.init()`方法,這個方法的作用是調用`Bicycle`的父類`Vehicle`。這樣可以確保`Bicycle`在修改屬性之前它所繼承的屬性`numberOfWheels`能被`Vehicle`類初始化。在調用`super.init()`之后,原本的屬性`numberOfWheels`被賦值為`2`。
如果你創建一個`Bicycle`實例,你可以調用繼承的`description`計算型屬性去查看屬性`numberOfWheels`是否有改變。
~~~
let bicycle = Bicycle()
print("Bicycle: \(bicycle.description)")
// Bicycle: 2 wheel(s)
~~~
> 注意 子類可以在初始化時修改繼承變量屬性,但是不能修改繼承過來的常量屬性。
### 自動構造器的繼承
如上所述,子類不會默認繼承父類的構造器。但是如果特定條件可以滿足,父類構造器是可以被自動繼承的。在實踐中,這意味著對于許多常見場景你不必重寫父類的構造器,并且在盡可能安全的情況下以最小的代價來繼承父類的構造器。
假設要為子類中引入的任意新屬性提供默認值,請遵守以下2個規則:
#### 規則 1
如果子類沒有定義任何指定構造器,它將自動繼承所有父類的指定構造器。
#### 規則 2
如果子類提供了所有父類指定構造器的實現--不管是通過規則1繼承過來的,還是通過自定義實現的--它將自動繼承所有父類的便利構造器。
即使你在子類中添加了更多的便利構造器,這兩條規則仍然適用。
> 注意:
> 子類可以通過部分滿足規則2的方式,使用子類便利構造器來實現父類的指定構造器。
### 指定構造器和便利構造器操作
接下來的例子將在操作中展示指定構造器、便利構造器和自動構造器的繼承。它定義了包含三個類`Food`、`RecipeIngredient`以及`ShoppingListItem`的類層次結構,并將演示它們的構造器是如何相互作用的。
類層次中的基類是`Food`,它是一個簡單的用來封裝食物名字的類。`Food`類引入了一個叫做`name`的`String`類型屬性,并且提供了兩個構造器來創建`Food`實例:
~~~
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
~~~
下圖中展示了`Food`的構造器鏈:

類沒有提供一個默認的逐一成員構造器,所以`Food`類提供了一個接受單一參數`name`的指定構造器。這個構造器可以使用一個特定的名字來創建新的`Food`實例:
~~~
let namedMeat = Food(name: "Bacon")
// namedMeat 的名字是 "Bacon”
~~~
`Food`類中的構造器`init(name: String)`被定義為一個指定構造器,因為它能確保所有新`Food`實例的中存儲型屬性都被初始化。`Food`類沒有父類,所以`init(name: String)`構造器不需要調用`super.init()`來完成構造。
`Food`類同樣提供了一個沒有參數的便利構造器?`init()`。這個`init()`構造器為新食物提供了一個默認的占位名字,通過代理調用同一類中定義的指定構造器`init(name: String)`并給參數`name`傳值`[Unnamed]`來實現:
~~~
let mysteryMeat = Food()
// mysteryMeat 的名字是 [Unnamed]
~~~
類層級中的第二個類是`Food`的子類`RecipeIngredient`。`RecipeIngredient`類構建了食譜中的一味調味劑。它引入了`Int`類型的數量屬性`quantity`(以及從`Food`繼承過來的`name`屬性),并且定義了兩個構造器來創建`RecipeIngredient`實例:
~~~
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`類的構造器鏈:

`RecipeIngredient`類擁有一個指定構造器`init(name: String, quantity: Int)`,它可以用來產生新`RecipeIngredient`實例的所有屬性值。這個構造器一開始先將傳入的`quantity`參數賦值給`quantity`屬性,這個屬性也是唯一在`RecipeIngredient`中新引入的屬性。隨后,構造器將任務向上代理給父類`Food`的`init(name: String)`。這個過程滿足[兩段式構造過程](http://wiki.jikexueyuan.com/project/swift/chapter2/14_Initialization.html#two_phase_initialization)中的安全檢查1。
`RecipeIngredient`也定義了一個便利構造器`init(name: String)`,它只通過`name`來創建`RecipeIngredient`的實例。這個便利構造器假設任意`RecipeIngredient`實例的`quantity`為1,所以不需要顯示指明數量即可創建出實例。這個便利構造器的定義可以讓創建實例更加方便和快捷,并且避免了使用重復的代碼來創建多個`quantity`為 1 的`RecipeIngredient`實例。這個便利構造器只是簡單的將任務代理給了同一類里提供的指定構造器。
注意,`RecipeIngredient`的便利構造器`init(name: String)`使用了跟`Food`中指定構造器`init(name: String)`相同的參數。因為這個便利構造器重寫要父類的指定構造器`init(name: String)`,必須在前面使用使用`override`標識(參見[構造器的繼承和重寫](http://wiki.jikexueyuan.com/project/swift/chapter2/14_Initialization.html#initializer_inheritance_and_overriding))。
在這個例子中,`RecipeIngredient`的父類是`Food`,它有一個便利構造器`init()`。這個構造器因此也被`RecipeIngredient`繼承。這個繼承的`init()`函數版本跟`Food`提供的版本是一樣的,除了它是將任務代理給`RecipeIngredient`版本的`init(name: String)`而不是`Food`提供的版本。
所有的這三種構造器都可以用來創建新的`RecipeIngredient`實例:
~~~
let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
~~~
類層級中第三個也是最后一個類是`RecipeIngredient`的子類,叫做`ShoppingListItem`。這個類構建了購物單中出現的某一種調味料。
購物單中的每一項總是從`unpurchased`未購買狀態開始的。為了展現這一事實,`ShoppingListItem`引入了一個布爾類型的屬性`purchased`,它的默認值是`false`。`ShoppingListItem`還添加了一個計算型屬性`description`,它提供了關于`ShoppingListItem`實例的一些文字描述:
~~~
class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name.lowercaseString)"
output += purchased ? " ?" : " ?"
return output
}
}
~~~
> 注意:
> `ShoppingListItem`沒有定義構造器來為`purchased`提供初始化值,這是因為任何添加到購物單的項的初始狀態總是未購買。
由于它為自己引入的所有屬性都提供了默認值,并且自己沒有定義任何構造器,`ShoppingListItem`將自動繼承所有父類中的指定構造器和便利構造器。
下圖種展示了所有三個類的構造器鏈:

你可以使用全部三個繼承來的構造器來創建`ShoppingListItem`的新實例:
~~~
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 ?
~~~
如上所述,例子中通過字面量方式創建了一個新數組`breakfastList`,它包含了三個新的`ShoppingListItem`實例,因此數組的類型也能自動推導為`ShoppingListItem[]`。在數組創建完之后,數組中第一個`ShoppingListItem`實例的名字從`[Unnamed]`修改為`Orange juice`,并標記為已購買。接下來通過遍歷數組每個元素并打印它們的描述值,展示了所有項當前的默認狀態都已按照預期完成了賦值。
## 可失敗構造器
如果一個類,結構體或枚舉類型的對象,在構造自身的過程中有可能失敗,則為其定義一個可失敗構造器,是非常有必要的。這里所指的“失敗”是指,如給構造器傳入無效的參數值,或缺少某種所需的外部資源,又或是不滿足某種必要的條件等。
為了妥善處理這種構造過程中可能會失敗的情況。你可以在一個類,結構體或是枚舉類型的定義中,添加一個或多個可失敗構造器。其語法為在`init`關鍵字后面加添問號`(init?)`。
> 注意: 可失敗構造器的參數名和參數類型,不能與其它非可失敗構造器的參數名,及其類型相同。
可失敗構造器,在構建對象的過程中,創建一個其自身類型為可選類型的對象。你通過`return nil`?語句,來表明可失敗構造器在何種情況下“失敗”。
> 注意: 嚴格來說,構造器都不支持返回值。因為構造器本身的作用,只是為了能確保對象自身能被正確構建。所以即使你在表明可失敗構造器,失敗的這種情況下,用到了`return nil`。也不要在表明可失敗構造器成功的這種情況下,使用關鍵字?`return`。
下例中,定義了一個名為`Animal`的結構體,其中有一個名為`species`的,`String`類型的常量屬性。同時該結構體還定義了一個,帶一個`String`類型參數`species`的,可失敗構造器。這個可失敗構造器,被用來檢查傳入的參數是否為一個空字符串,如果為空字符串,則該可失敗構造器,構建對象失敗,否則成功。
~~~
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
~~~
你可以通過該可失敗構造器來構建一個Animal的對象,并檢查其構建過程是否成功。
~~~
let someCreature = Animal(species: "Giraffe")
// someCreature 的類型是 Animal? 而不是 Animal
if let giraffe = someCreature {
print("An animal was initialized with a species of \(giraffe.species)")
}
// 打印 "An animal was initialized with a species of Giraffe"
~~~
如果你給該可失敗構造器傳入一個空字符串作為其參數,則該可失敗構造器失敗。
~~~
let anonymousCreature = Animal(species: "")
// anonymousCreature 的類型是 Animal?, 而不是 Animal
if anonymousCreature == nil {
print("The anonymous creature could not be initialized")
}
// 打印 "The anonymous creature could not be initialized"
~~~
> 注意: 空字符串(`""`)和一個值為`nil`的可選類型的字符串是兩個完全不同的概念。上例中的空字符串(`""`)其實是一個有效的,非可選類型的字符串。這里我們只所以讓`Animal`的可失敗構造器,構建對象失敗,只是因為對于`Animal`這個類的`species`屬性來說,它更適合有一個具體的值,而不是空字符串。
### 枚舉類型的可失敗構造器
你可以通過構造一個帶一個或多個參數的可失敗構造器來獲取枚舉類型中特定的枚舉成員。還能在參數不滿足你所期望的條件時,導致構造失敗。
下例中,定義了一個名為TemperatureUnit的枚舉類型。其中包含了三個可能的枚舉成員(`Kelvin`,`Celsius`,和`Fahrenheit`)和一個被用來找到`Character`值所對應的枚舉成員的可失敗構造器:
~~~
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
}
}
}
~~~
你可以通過給該可失敗構造器傳遞合適的參數來獲取這三個枚舉成員中相匹配的其中一個枚舉成員。當參數的值不能與任意一枚舉成員相匹配時,該枚舉類型的構建過程失敗:
~~~
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
// 打印 "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.")
}
// 打印 "This is not a defined temperature unit, so initialization failed."
~~~
### 帶原始值的枚舉類型的可失敗構造器
帶原始值的枚舉類型會自帶一個可失敗構造器`init?(rawValue:)`,該可失敗構造器有一個名為`rawValue`的默認參數,其類型和枚舉類型的原始值類型一致,如果該參數的值能夠和枚舉類型成員所帶的原始值匹配,則該構造器構造一個帶此原始值的枚舉成員,否則構造失敗。
因此上面的 TemperatureUnit的例子可以重寫為:
~~~
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."
~~~
### 類的可失敗構造器
值類型(如結構體或枚舉類型)的可失敗構造器,對何時何地觸發構造失敗這個行為沒有任何的限制。比如在前面的例子中,結構體`Animal`的可失敗構造器觸發失敗的行為,甚至發生在`species`屬性的值被初始化以前。而對類而言,就沒有那么幸運了。類的可失敗構造器只能在所有的類屬性被初始化后和所有類之間的構造器之間的代理調用發生完后觸發失敗行為。
下例子中,定義了一個名為`Product`的類,其內部結構和結構體`Animal`很相似,內部也有一個名為`name`的`String`類型的屬性。由于該屬性的值同樣不能為空字符串,所以我們加入了可失敗構造器來確保該類滿足上述條件。但由于`Product`類不是一個結構體,所以當想要在該類中添加可失敗構造器觸發失敗條件時,必須確保`name`屬性被初始化。因此我們把`name`屬性的`String`類型做了一點點小小的修改,把其改為隱式解析可選類型(`String!`),來確保可失敗構造器觸發失敗條件時,所有類屬性都被初始化了。因為所有可選類型都有一個默認的初始值`nil`。因此最后`Product`類可寫為:
~~~
class Product {
let name: String!
init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
}
~~~
因為`name`屬性是一個常量,所以一旦`Product`類構造成功,`name`屬性肯定有一個非`nil`的值。因此完全可以放心大膽的直接訪問`Product`類的`name`屬性,而不用考慮去檢查`name`屬性是否有值。
~~~
if let bowTie = Product(name: "bow tie") {
// 不需要檢查 bowTie.name == nil
print("The product's name is \(bowTie.name)")
}
// 打印 "The product's name is bow tie"
~~~
### 構造失敗的傳遞
可失敗構造器同樣滿足在[構造器鏈](http://wiki.jikexueyuan.com/project/swift/chapter2/14_Initialization.html#initialization_chain)中所描述的構造規則。其允許在同一類,結構體和枚舉中橫向代理其他的可失敗構造器。類似的,子類的可失敗構造器也能向上代理基類的可失敗構造器。
無論是向上代理還是橫向代理,如果你代理的可失敗構造器,在構造過程中觸發了構造失敗的行為,整個構造過程都將被立即終止,接下來任何的構造代碼都將不會被執行。
> 注意: 可失敗構造器也可以代理調用其它的非可失敗構造器。通過這個方法,你可以為已有的構造過程加入構造失敗的條件。
下面這個例子,定義了一個名為`CartItem`的`Product`類的子類。這個類建立了一個在線購物車中的物品的模型,它有一個名為`quantity`的常量參數,用來表示該物品的數量至少為1:
~~~
class CartItem: Product {
let quantity: Int!
init?(name: String, quantity: Int) {
super.init(name: name)
if quantity < 1 { return nil }
self.quantity = quantity
}
}
~~~
和`Product`類中的`name`屬性相類似的,`CartItem`類中的`quantity`屬性的類型也是一個隱式解析可選類型,只不過由(`String!`)變為了(`Int!`)。這樣做都是為了確保在構造過程中,該屬性在被賦予特定的值之前能有一個默認的初始值nil。
可失敗構造器總是先向上代理調用基類,`Product`的構造器?`init(name:)`。這滿足了可失敗構造器在觸發構造失敗這個行為前必須總是執行構造代理調用這個條件。
如果由于`name`的值為空而導致基類的構造器在構造過程中失敗。則整個`CartIem`類的構造過程都將失敗,后面的子類的構造過程都將不會被執行。如果基類構建成功,則繼續運行子類的構造器代碼。
如果你構造了一個`CartItem`對象,并且該對象的`name`屬性不為空以及`quantity`屬性為1或者更多,則構造成功:
~~~
if let twoSocks = CartItem(name: "sock", quantity: 2) {
print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
// 打印 "Item: sock, quantity: 2"
~~~
如果你構造一個`CartItem`對象,其`quantity`的值`0`, 則`CartItem`的可失敗構造器觸發構造失敗的行為:
~~~
if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
print("Unable to initialize zero shirts")
}
// 打印 "Unable to initialize zero shirts"
~~~
類似的, 如果你構造一個`CartItem`對象,但其`name`的值為空, 則基類`Product`的可失敗構造器將觸發構造失敗的行為,整個`CartItem`的構造行為同樣為失敗:
~~~
if let oneUnnamed = CartItem(name: "", quantity: 1) {
print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
print("Unable to initialize one unnamed product")
}
// 打印 "Unable to initialize one unnamed product"
~~~
### 重寫一個可失敗構造器
就如同其它構造器一樣,你也可以用子類的可失敗構造器重寫基類的可失敗構造器。或者你也可以用子類的非可失敗構造器重寫一個基類的可失敗構造器。這樣做的好處是,即使基類的構造器為可失敗構造器,但當子類的構造器在構造過程不可能失敗時,我們也可以把它修改過來。
注意當你用一個子類的非可失敗構造器重寫了一個父類的可失敗構造器時,子類的構造器將不再能向上代理父類的可失敗構造器。一個非可失敗的構造器永遠也不能代理調用一個可失敗構造器。
> 注意: 你可以用一個非可失敗構造器重寫一個可失敗構造器,但反過來卻行不通。
下例定義了一個名為`Document`的類,這個類中的`name`屬性允許為`nil`和一個非空字符串,但不能是一個空字符串:
~~~
class Document {
var name: String?
// 該構造器構建了一個name屬性值為nil的document對象
init() {}
// 該構造器構建了一個name屬性值為非空字符串的document對象
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
~~~
下面這個例子,定義了一個名為`AutomaticallyNamedDocument`的`Document`類的子類。這個子類重寫了基類的兩個指定構造器。確保了不論在何種情況下`name`屬性總是有一個非空字符串`[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`用一個非可失敗構造器`init(name:)`,重寫了基類的可失敗構造器`init?(name:)`。因為子類用不同的方法處理了`name`屬性的值為一個空字符串的這種情況。所以子類將不再需要一個可失敗的構造器。
### 可失敗構造器 init!
通常來說我們通過在`init`關鍵字后添加問號的方式來定義一個可失敗構造器,但你也可以使用通過在`init`后面添加驚嘆號的方式來定義一個可失敗構造器`(init!)`,該可失敗構造器將會構建一個特定類型的隱式解析可選類型的對象。
你可以在?`init?`構造器中代理調用?`init!`構造器,反之亦然。 你也可以用?`init?`重寫?`init!`,反之亦然。 你還可以用`init`代理調用`init!`,但這會觸發一個斷言:是否?`init!`?構造器會觸發構造失敗?
## 必要構造器
在類的構造器前添加?`required`?修飾符表明所有該類的子類都必須實現該構造器:
~~~
class SomeClass {
required init() {
// 在這里添加該必要構造器的實現代碼
}
}
~~~
當子類重寫基類的必要構造器時,必須在子類的構造器前同樣添加`required`修飾符以確保當其它類繼承該子類時,該構造器同為必要構造器。在重寫基類的必要構造器時,不需要添加`override`修飾符:
~~~
class SomeSubclass: SomeClass {
required init() {
// 在這里添加子類必要構造器的實現代碼
}
}
~~~
> 注意: 如果子類繼承的構造器能滿足必要構造器的需求,則你無需顯示的在子類中提供必要構造器的實現。
## 通過閉包和函數來設置屬性的默認值
如果某個存儲型屬性的默認值需要特別的定制或準備,你就可以使用閉包或全局函數來為其屬性提供定制的默認值。每當某個屬性所屬的新類型實例創建時,對應的閉包或函數會被調用,而它們的返回值會當做默認值賦值給這個屬性。
這種類型的閉包或函數一般會創建一個跟屬性類型相同的臨時變量,然后修改它的值以滿足預期的初始狀態,最后將這個臨時變量的值作為屬性的默認值進行返回。
下面列舉了閉包如何提供默認值的代碼概要:
~~~
class SomeClass {
let someProperty: SomeType = {
// 在這個閉包中給 someProperty 創建一個默認值
// someValue 必須和 SomeType 類型相同
return someValue
}()
}
~~~
注意閉包結尾的大括號后面接了一對空的小括號。這是用來告訴 Swift 需要立刻執行此閉包。如果你忽略了這對括號,相當于是將閉包本身作為值賦值給了屬性,而不是將閉包的返回值賦值給屬性。
> 注意:
> 如果你使用閉包來初始化屬性的值,請記住在閉包執行時,實例的其它部分都還沒有初始化。這意味著你不能夠在閉包里訪問其它的屬性,就算這個屬性有默認值也不允許。同樣,你也不能使用隱式的`self`屬性,或者調用其它的實例方法。
下面例子中定義了一個結構體`Checkerboard`,它構建了西洋跳棋游戲的棋盤:

西洋跳棋游戲在一副黑白格交替的 10x10 的棋盤中進行。為了呈現這副游戲棋盤,`Checkerboard`結構體定義了一個屬性`boardColors`,它是一個包含 100 個布爾值的數組。數組中的某元素布爾值為`true`表示對應的是一個黑格,布爾值為`false`表示對應的是一個白格。數組中第一個元素代表棋盤上左上角的格子,最后一個元素代表棋盤上右下角的格子。
`boardColor`數組是通過一個閉包來初始化和組裝顏色值的:
~~~
struct Checkerboard {
let boardColors: [Bool] = {
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...10 {
for j in 1...10 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
return temporaryBoard
}()
func squareIsBlackAtRow(row: Int, column: Int) -> Bool {
return boardColors[(row * 10) + column]
}
}
~~~
每當一個新的`Checkerboard`實例創建時,對應的賦值閉包會執行,一系列顏色值會被計算出來作為默認值賦值給`boardColors`。上面例子中描述的閉包將計算出棋盤中每個格子合適的顏色,將這些顏色值保存到一個臨時數組`temporaryBoard`中,并在構建完成時將此數組作為閉包返回值返回。這個返回的值將保存到`boardColors`中,并可以通`squareIsBlackAtRow`這個工具函數來查詢。
~~~
let board = Checkerboard()
print(board.squareIsBlackAtRow(0, column: 1))
// 輸出 "true"
print(board.squareIsBlackAtRow(9, column: 9))
// 輸出 "false"
~~~
- 介紹
- 歡迎使用 Swift
- 關于 Swift
- Swift 初見
- Swift 版本歷史記錄
- Swift1.0 發布內容
- Swift 教程
- 基礎部分
- 基本運算符
- 字符串和字符
- 集合類型
- 控制流
- 函數
- 閉包
- 枚舉
- 類和結構體
- 屬性
- 方法
- 下標腳本
- 繼承
- 構造過程
- 析構過程
- 自動引用計數
- 可選鏈
- 錯誤處理
- 類型轉換
- 嵌套類型
- 擴展
- 協議
- 泛型
- 權限控制
- 高級操作符
- 語言參考
- 關于語言參考
- 詞法結構
- 類型
- 表達式
- 語句
- 聲明
- 特性
- 模式
- 泛型參數
- 語法總結
- 蘋果官方Blog官方翻譯
- Access Control 權限控制的黑與白
- 造個類型不是夢-白話Swift類型創建
- WWDC里面的那個“大炮打氣球”
- Swift與C語言指針友好合作
- 引用類型和值類型的恩怨
- 訪問控制和Protected
- 可選類型完美解決占位問題