iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 9
0
自我挑戰組

iOS 新手開發的大小事系列 第 9

Day 9: [Swift] 下標 (Subscripts)

  • 分享至 

  • xImage
  •  

前言

原本沒有打算要寫這個標題的,因為目前接觸到的機會並不多,也比較少看到有人在使用,但後來想想,寫了才會加深自己的印象,下次看到此種語法也比較會有熟悉的感覺,於是就誕生了今天的主題,就表面意思還有官方網站的陳述似乎沒有很好理解,不過其實就是一種對類、結構和列舉取值的方法。

類、結構和列舉可以定義下標 (Subscripts),這些下標是訪問集合、列表或序列中成員元素的快捷方式。使用下標按索引設置和檢索值,而無需使用單獨的方法進行設置和檢索。例如,可以將 Array 實例中的元素作為 someArray[index] 訪問,並將 Dictionary 實例中的元素作為 someDictionary[key] 訪問。

可以為單個型別定義多個下標,然後根據傳遞給下標的索引值的型別選擇要使用的適當下標重載。下標不限於單個維度,可以定義具有多個輸入參數的下標以滿足自定義型別的需求。


下標語法 (Subscript Syntax)

下標能夠透過在實例名稱後的方括號中寫入一個或多個值來查詢型別的實例。它們的語法與實例方法語法和計算屬性語法相似。使用關鍵字 subscript 編寫下標定義,並以與實例方法相同的方式指定一個或多個輸入參數和一個返回型別。與實例方法不同,下標可以是讀寫的或唯讀的。此行為由 getter 和 setter 傳遞,其方式與計算屬性相同:

subscript(index: Int) -> Int {
    get {
        // Return an appropriate subscript value here.
    }
    set(newValue) {
        // Perform a suitable setting action here.
    }
}

newValue 的型別與下標的返回值相同。與計算屬性一樣,可以選擇不指定設置器的 (newValue) 參數。如果自己不提供一個預設參數,則名為 newValue 的預設參數會提供給設置器 (setter)。

與唯讀計算屬性一樣,可以通過刪除 get 關鍵字及其大括號來簡化唯讀下標的宣告:

subscript(index: Int) -> Int {
    // Return an appropriate subscript value here.
}

這是一個唯讀下標實現的範例,該範例定義了一個 TimesTable 結構來表示整數的 n 次表:

struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")
// Prints "six times three is 18"

在此範例中,創建了一個 TimesTable 新實例來表示三倍表。這是將值 3 傳遞給結構的初始值設定項來表示的,該值用於實例的 multiplier 參數。

可以透過調用下標來查詢 threeTimesTable 實例,如對threeTimesTable[6] 的調用所示。這將請求三倍表中的第六個條目,該條目返回值 18 或 3 乘以 6。

n 次表基於固定的數學規則。將 ThreeTimesTable[someIndex] 設置為新值是不合適的,因此 TimesTable 的下標定義為唯讀下標。


Subscript Usage (下標用法)

「下標」的確切含義取決於使用它的上下文。下標通常用作訪問集合、列表或序列中的成員元素的快捷方式。可以根據自己的特定類或結構的功能,以最合適的方式自由實現下標。

例如,Swift 的 Dictionary 型別實現下標來設置和檢索儲存在 Dictionary 實例中的值。可以在字典中設置值,方法是在下標括號內提供字典索引型別的索引,然後將字典的值型別的值分配給下標:

var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
numberOfLegs["bird"] = 2

上面的範例定義了一個名為 numberOfLegs 的變數,並使用包含三個索引值對的字典文字對其進行了初始化。numberOfLegs 字典的型別推斷為 [String:Int]。創建字典後,此範例使用下標指定向字典添加 String 關鍵字 “bird” 和 Int 值 2。

Swift 的 Dictionary 型別將其索引值下標實現為採用並返回可選型別的下標。對於上面的 numberOfLegs 詞典,索引值下標採用並返回 Int? 型別的值,或 “optional int” 型別。字典型別使用可選的下標型別來建模,並非每個索引都將具有值的事實,並透過為該索引分配 nil 值來提供一種刪除索引的值的方法。


下標選項 (Subscript Options)

下標可以採用任意數量的輸入參數,並且這些輸入參數可以是任何型別。下標也可以返回任何型別。下標可以使用可變參數,但不能使用 in-out 參數或提供預設參數值。

一個類或結構可以根據需要提供盡可能多的下標實現,並且將根據使用下標時在下標括號內包含的一個或多個值的型別來推斷要使用的適當下標。多個下標的定義稱為下標重載 (subscript overloading)。

雖然下標採用單個參數是最常見的,但是如果適合的型別,則還可以定義帶有多個參數的下標。下面的示例定義一個 Matrix 結構,該結構表示 Double 值的二維矩陣。矩陣結構的下標帶有兩個整數參數:

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 * columns 型別為 Double 的值。矩陣中的每個位置的初始值均為 0.0。為此,將數組大小和初始單元格值 0.0 傳遞給數組初始化程序,該初始化程序創建並初始化一個正確大小的新數組。

以通過將適當的行數和列數傳遞給其初始化程序來構造新的 Matrix 實例:

var matrix = Matrix(rows: 2, columns: 2)

上面的範例創建了一個具有兩行兩列的新 Matrix 實例。此 Matrix 實例的網格數組實際上是矩陣的扁平版本,從左上角到右下角讀取:

可以通過將行和列值傳遞到下標(以逗號分隔)來設置矩陣中的值:

matrix[0, 1] = 1.5
matrix[1, 0] = 3.2

這兩個語句調用下標的設置器 (setter),以在矩陣的右上位置(行為 0,列為 1)設置值為 1.5,在左下角位置(行為 1,列為 0)設置值為 3.2:

Matrix 下標的 getter 和 setter 都包含一個聲稱 (assert),以檢查下標的行和列值是否有效。為了幫助進行這些宣告,Matrix 包含了一個便捷的方法,稱為 indexIsValid(row:column:),該方法檢查所請求的行和列是否在矩陣的範圍內:

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

如果嘗試訪問矩陣範圍之外的下標,則會觸發一個聲稱:

let someValue = matrix[2, 2]
// This triggers an assert, because [2, 2] is outside of the matrix bounds.

型別下標 (Type Subscripts)

如上所述,實例下標是在特定型別的實例上調用的下標。還可以定義在型別本身上調用的下標。這種下標稱為型別下標。可以透過在下標關鍵字之前寫靜態關鍵字來指示類型下標。類可以改用 class 關鍵字,以允許子類覆蓋父類對該下標的實現。以下範例顯示如何定義和調用型別下標:

enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
    static subscript(n: Int) -> Planet {
        return Planet(rawValue: n)!
    }
}
let mars = Planet[4]
print(mars)

上一篇
Day 8: [Swift] 型別轉換 (Type Casting)
下一篇
Day 10: [Swift] 結構和類 (Struct and Class)
系列文
iOS 新手開發的大小事30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言