Swift的下標可以定義在類、結構體和列舉中,是訪問集合、列表或序列中元素的快捷方式。可以使用下標的索引,設定和獲取值,而不需要再呼叫對應的存取方法。舉例來說,用下標訪問一個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
的值。
可以通過下標訪問TimesTable
例項,例如上面演示的threeTimeTable[6]
。這條語句查詢了3
的乘法表中的第6個元素,返回3
的6
倍即18
。
注: timeTable
例子基於一個固定的數學表達公式,對threeTimesTa[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?
或者可選的nil
。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(repeating: 0.0, count: rows * columns)
}
func indexIsValid(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}
subscript(row: Int, column: Int) -> Double {
get {
assert(indexIsValid(row: row, column: column), "Index out of range")
return grid[(row * columns) + column]
}
set {
assert(indexIsValid(row: row, column: column), "Index out of range")
grid[(row * columns) + column] = newValue
}
}
}
複製程式碼
Matrix
提供了一個接受兩個入參的構造方法,入參分別是rows
和columns
,建立了一個足夠容納rows * columns
個Double
型別的值的陣列。通過傳入陣列長度和初始值0.0
到陣列的構造器,將矩陣中每個位置的值初始化為0.0
。
可以通過傳入合適的row
和column
的數量來構造一個新的Matrix
例項:
var matrix = Matrix(rows: 2, columns: 2)
複製程式碼
上例中建立了一個 Matrix
例項來表示兩行兩列的矩陣。該 Matrix
例項的 grid
陣列按照從左上到右下的閱讀順序將矩陣扁平化儲存。
將row
和column
的值傳入下標來為矩陣設值,下標的入參使用逗號分隔:
將 row
和 column
的值傳入下標來為矩陣設值,下標的入參使用逗號分隔:
matrix[0, 1] = 1.5
matrix[1, 0] = 3.2
複製程式碼
上面兩條語句分別呼叫下標的setter將矩陣右上角位置(即row
為0
、column
為1
的位置)的值設定為1.5
,將矩陣左下角位置(即row
為1
、column
為0
的位置)的值設定為3.2
:
Matrix
下標的getter和setter中都含有斷言,用來檢測下標入參row
和column
的值是否有效。為了方便進行斷言,Matrix
包含了一個名為indexIsValid(row:colimn:)
的便利方法,用來檢測入參row
和column
的值是否在矩陣範圍內:
func indexIsValid(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}
複製程式碼
斷言在下標越界時觸發:
let someValue = matrix[2, 2]
// 斷言將會觸發,因為 [2, 2] 已經超過了 matrix 的範圍
複製程式碼