Swift-下標

優質神經病發表於2018-07-27

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個元素,返回36倍即18

注: timeTable例子基於一個固定的數學表達公式,對threeTimesTa[someIndex]進行賦值操作並不合適,因此下標定義為只讀的。

下標用法

下標的確切含有取決於使用場景。下標通常會作為訪問集合,列表或序列中元素的快捷方式。可以針對自己特定的類或結構體的功能來自由的以最恰當的方式實現下標。

例如。Swift的Dictionary型別實現下標用於對其例項中儲存的值進行存取操作。作為字典設值時,在下標中使用和字典的鍵型別相同的鍵,並把一個和字典的值型別相同的值賦給這個下標:

var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
numberOfLegs["bird"] = 2
複製程式碼

上例定義一個名為numberOfLegs的變數,並用一個包含三對鍵值的字典字面量初始化它。numberOfLegs字典的型別被推斷為[String: Int]。字典建立完成後,該例子通過下標將String型別的鍵birdInt型別的值2新增到字典中。

注: Swift的Dictionary型別的下標接受並返回可選型別的值。上例中numberOfLegs字典通過下標返回的是一個Int?或者可選的nilDictionary型別之所以如此實現下標,是因為不是每個鍵都有個對應的值。同事這也提供了一種通過鍵刪除對應值的方式,只需要將鍵對應的值賦值為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提供了一個接受兩個入參的構造方法,入參分別是rowscolumns,建立了一個足夠容納rows * columnsDouble型別的值的陣列。通過傳入陣列長度和初始值0.0到陣列的構造器,將矩陣中每個位置的值初始化為0.0

可以通過傳入合適的rowcolumn的數量來構造一個新的Matrix例項:

var matrix = Matrix(rows: 2, columns: 2)
複製程式碼

上例中建立了一個 Matrix 例項來表示兩行兩列的矩陣。該 Matrix 例項的 grid 陣列按照從左上到右下的閱讀順序將矩陣扁平化儲存。

rowcolumn的值傳入下標來為矩陣設值,下標的入參使用逗號分隔:

rowcolumn 的值傳入下標來為矩陣設值,下標的入參使用逗號分隔:

matrix[0, 1] = 1.5
matrix[1, 0] = 3.2
複製程式碼

上面兩條語句分別呼叫下標的setter將矩陣右上角位置(即row0column1的位置)的值設定為1.5,將矩陣左下角位置(即row1column0的位置)的值設定為3.2

Matrix下標的getter和setter中都含有斷言,用來檢測下標入參rowcolumn的值是否有效。為了方便進行斷言,Matrix包含了一個名為indexIsValid(row:colimn:)的便利方法,用來檢測入參rowcolumn的值是否在矩陣範圍內:

func indexIsValid(row: Int, column: Int) -> Bool {
    return row >= 0 && row < rows && column >= 0 && column < columns
}
複製程式碼

斷言在下標越界時觸發:

let someValue = matrix[2, 2]
// 斷言將會觸發,因為 [2, 2] 已經超過了 matrix 的範圍
複製程式碼

相關文章