下標
下標可以定義在類、結構體和枚舉中,是訪問集合,列表或序列中元素的快捷方式。可以使用下標的索引,設置和獲取值,而不需要再調用對應的存取方法。舉例來說,用下標訪問一個Array實例中的元素可以寫作someArray[index],訪問Dictionary實例中的元素可以寫作someDictionary[key]。
一個類型可以定義多個下標,通過不同索引類型進行重載。下標不限於一維,你可以定義具有多個入參的下標滿足自定義類型的需求。
下標語法
下標允許你通過在實例名稱後面的方括號中傳入一個或者多個索引值來對實例進行存取。語法類似於實例方法語法和計算型屬性語法的混合。與定義實例方法類似,定義下標使用subscript關鍵字,指定一個或多個輸入參數和返回類型;與實例方法不同的是,下標可以設定爲讀寫或只讀。這種行爲由 getter 和 setter 實現,有點類似計算型屬性:
subscript(index: Int) -> Int {
get {
// 返回一個適當的 Int 類型的值
}
set(newValue) {
// 執行適當的賦值操作
}
}
newValue的類型和下標的返回類型相同。如同計算型屬性,可以不指定 setter 的參數(newValue)。如果不指定參數,setter 會提供一個名爲newValue的默認參數。
如同只讀計算型屬性,可以省略只讀下標的get關鍵字:
subscript(index: Int) -> Int {
// 返回一個適當的 Int 類型的值
}
下面代碼演示了只讀下標的實現,這裏定義了一個TimesTable結構體,用來表示傳入整數的乘法表:
struct TimesTable {
let multiplier: Int
subscript(index: Int) -> Int {
return multiplier * index
}
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")
// 打印 "six times three is 18"
在上例中,創建了一個TimesTable實例,用來表示整數3的乘法表。數值3被傳遞給結構體的構造函數,作爲實例成員multiplier的值。
你可以通過下標訪問threeTimesTable實例,例如上面演示的threeTimesTable[6]。這條語句查詢了3的乘法表中的第六個元素,返回3的6倍即18。
注意
TimesTable例子基於一個固定的數學公式,對threeTimesTable[someIndex]進行賦值操作並不合適,因此下標定義爲只讀的。
下標用法
下標的確切含義取決於使用場景。下標通常作爲訪問集合,列表或序列中元素的快捷方式。你可以針對自己特定的類或結構體的功能來自由地以最恰當的方式實現下標。
例如,Swift 的Dictionary類型實現下標用於對其實例中儲存的值進行存取操作。爲字典設值時,在下標中使用和字典的鍵類型相同的鍵,並把一個和字典的值類型相同的值賦給這個下標:
var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
numberOfLegs["bird"] = 2
上例定義一個名爲numberOfLegs的變量,並用一個包含三對鍵值的字典字面量初始化它。numberOfLegs字典的類型被推斷爲[String: Int]。字典創建完成後,該例子通過下標將String類型的鍵bird和Int類型的值2添加到字典中。
注意
Swift 的Dictionary類型的下標接受並返回可選類型的值。上例中的numberOfLegs字典通過下標返回的是一個Int?或者說“可選的int”。Dictionary類型之所以如此實現下標,是因爲不是每個鍵都有個對應的值,同時這也提供了一種通過鍵刪除對應值的方式,只需將鍵對應的值賦值爲nil即可。
下標選項
下標可以接受任意數量的入參,並且這些入參可以是任意類型。下標的返回值也可以是任意類型。下標可以使用變量參數和可變參數,但不能使用輸入輸出參數,也不能給參數設置默認值。
一個類或結構體可以根據自身需要提供多個下標實現,使用下標時將通過入參的數量和類型進行區分,自動匹配合適的下標,這就是下標的重載。
雖然接受單一入參的下標是最常見的,但也可以根據情況定義接受多個入參的下標。例如下例定義了一個Matrix結構體,用於表示一個Double類型的二維矩陣。Matrix結構體的下標接受兩個整型參數:
struct Matrix {
let rows: Int, columns: Int
var grid: [Double]
init(rows: Int, columns: Int) {
self.rows = rows
self.columns = columns
grid = Array(count: rows * columns, repeatedValue: 0.0)
}
func indexIsValidForRow(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}
subscript(row: Int, column: Int) -> Double {
get {
assert(indexIsValidForRow(row, column: column), "Index out of range")
return grid[(row * columns) + column]
}
set {
assert(indexIsValidForRow(row, column: column), "Index out of range")
grid[(row * columns) + column] = newValue
}
}
}
你可以通過傳入合適的row和column的數量來構造一個新的Matrix實例:
var matrix = Matrix(rows: 2, columns: 2)
Matrix下標的 getter 和 setter 中都含有斷言,用來檢查下標入參row和column的值是否有效。爲了方便進行斷言,Matrix包含了一個名爲indexIsValidForRow(_:column:)的便利方法,用來檢查入參row和column的值是否在矩陣範圍內:
func indexIsValidForRow(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}
斷言在下標越界時觸發:
let someValue = matrix[2, 2]
// 斷言將會觸發,因爲 [2, 2] 已經超過了 matrix 的範圍