這里的教程為Swift官方教程中文版。

下標(biāo)

下標(biāo)可以定義在類、結(jié)構(gòu)體和枚舉中,是訪問(wèn)集合、列表或序列中元素的快捷方式??梢允褂孟聵?biāo)的索引,設(shè)置和獲取值,而不需要再調(diào)用對(duì)應(yīng)的存取方法。舉例來(lái)說(shuō),用下標(biāo)訪問(wèn)一個(gè) Array 實(shí)例中的元素可以寫作 someArray[index],訪問(wèn) Dictionary 實(shí)例中的元素可以寫作 someDictionary[key]。

一個(gè)類型可以定義多個(gè)下標(biāo),通過(guò)不同索引類型進(jìn)行對(duì)應(yīng)的重載。下標(biāo)不限于一維,你可以定義具有多個(gè)入?yún)⒌南聵?biāo)滿足自定義類型的需求。

下標(biāo)語(yǔ)法

下標(biāo)允許你通過(guò)在實(shí)例名稱后面的方括號(hào)中傳入一個(gè)或者多個(gè)索引值來(lái)對(duì)實(shí)例進(jìn)行查詢。它的語(yǔ)法類似于實(shí)例方法語(yǔ)法和計(jì)算型屬性語(yǔ)法。定義下標(biāo)使用 subscript 關(guān)鍵字,與定義實(shí)例方法類似,都是指定一個(gè)或多個(gè)輸入?yún)?shù)和一個(gè)返回類型。與實(shí)例方法不同的是,下標(biāo)可以設(shè)定為讀寫或只讀。這種行為由 getter 和 setter 實(shí)現(xiàn),類似計(jì)算型屬性:

subscript(index: Int) -> Int {
    get {
      // 返回一個(gè)適當(dāng)?shù)?Int 類型的值
    }
    set(newValue) {
      // 執(zhí)行適當(dāng)?shù)馁x值操作
    }
}

newValue 的類型和下標(biāo)操作的返回類型相同。如同計(jì)算型屬性,可以不指定 setter 的參數(shù)(newValue)。如果不指定參數(shù),setter 會(huì)提供一個(gè)名為 newValue 的默認(rèn)參數(shù)。

如同只讀計(jì)算型屬性,對(duì)于只讀下標(biāo)的聲明,你可以通過(guò)省略 get 關(guān)鍵字和對(duì)應(yīng)的大括號(hào)組來(lái)進(jìn)行簡(jiǎn)寫:

subscript(index: Int) -> Int {
    // 返回一個(gè)適當(dāng)?shù)?Int 類型的值
}

下面代碼演示了只讀下標(biāo)的實(shí)現(xiàn),這里定義了一個(gè) TimesTable 結(jié)構(gòu)體,用來(lái)表示對(duì)應(yīng)整數(shù)的乘法表:

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”

在上例中,創(chuàng)建了一個(gè) TimesTable 實(shí)例,用來(lái)表示整數(shù) 3 的乘法表。數(shù)值 3 被傳遞給結(jié)構(gòu)體的構(gòu)造函數(shù),作為實(shí)例成員 multiplier 的值。

你可以通過(guò)下標(biāo)訪問(wèn) threeTimesTable 實(shí)例,例如上面演示的 threeTimesTable[6]。這條語(yǔ)句查詢了乘法表中 3 的第六個(gè)元素,返回 36 倍即 18。

注意

TimesTable 例子基于一個(gè)固定的數(shù)學(xué)公式,對(duì) threeTimesTable[someIndex] 進(jìn)行賦值操作并不合適,因此下標(biāo)定義為只讀的。

下標(biāo)用法

“下標(biāo)”的確切含義取決于使用場(chǎng)景。下標(biāo)通常作為訪問(wèn)集合,列表或序列中元素的快捷方式。你可以針對(duì)自己特定的類或結(jié)構(gòu)體功能來(lái)以最恰當(dāng)?shù)姆绞綄?shí)現(xiàn)下標(biāo)。

例如,Swift 的 Dictionary 類型實(shí)現(xiàn)下標(biāo)用于對(duì)實(shí)例中儲(chǔ)存的值進(jìn)行存取操作。為字典設(shè)值時(shí),在下標(biāo)中使用和字典的鍵類型相同的鍵,并把一個(gè)和字典的值類型相同的值賦給這個(gè)下標(biāo):

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

上例定義一個(gè)名為 numberOfLegs 的變量,并用一個(gè)包含三對(duì)鍵值的字典字面量初始化它。numberOfLegs 字典的類型被推斷為 [String: Int]。字典創(chuàng)建完成后,該例子通過(guò)下標(biāo)將 String 類型的鍵 birdInt 類型的值 2 添加到字典中。

更多關(guān)于 Dictionary 下標(biāo)的信息請(qǐng)參考 讀取和修改字典。

注意

Swift 的 Dictionary 類型的下標(biāo)接受并返回可選類型的值。上例中的 numberOfLegs 字典通過(guò)下標(biāo)返回的是一個(gè) Int? 或者說(shuō)“可選的 int”。Dictionary 類型之所以如此實(shí)現(xiàn)下標(biāo),是因?yàn)椴皇敲總€(gè)鍵都有對(duì)應(yīng)的值,同時(shí)這也提供了一種通過(guò)鍵刪除對(duì)應(yīng)值的方式,只需將鍵對(duì)應(yīng)的值賦值為 nil 即可。

下標(biāo)選項(xiàng)

下標(biāo)可以接受任意數(shù)量的入?yún)?,并且這些入?yún)⒖梢允侨我忸愋?。下?biāo)的返回值也可以是任意類型。下標(biāo)可以使用可變參數(shù),但是不能使用 in-out 參數(shù)以及不能提供默認(rèn)參數(shù)。

一個(gè)類或結(jié)構(gòu)體可以根據(jù)自身需要提供多個(gè)下標(biāo)實(shí)現(xiàn),使用下標(biāo)時(shí)將通過(guò)入?yún)⒌臄?shù)量和類型進(jìn)行區(qū)分,自動(dòng)匹配合適的下標(biāo)。它通常被稱為下標(biāo)的重載。

雖然接受單一入?yún)⒌南聵?biāo)是最常見(jiàn)的,但也可以根據(jù)情況定義接受多個(gè)入?yún)⒌南聵?biāo)。例如下例定義了一個(gè) Matrix 結(jié)構(gòu)體,用于表示一個(gè) Double 類型的二維矩陣。Matrix 結(jié)構(gòu)體的下標(biāo)接受兩個(gè)整型參數(shù):

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 提供了一個(gè)接受兩個(gè)入?yún)⒌臉?gòu)造方法,入?yún)⒎謩e是 rowscolumns,創(chuàng)建了一個(gè)足夠容納 rows * columns 個(gè) Double 類型的值的數(shù)組。通過(guò)傳入數(shù)組長(zhǎng)度和初始值 0.0 到數(shù)組的構(gòu)造器,將矩陣中每個(gè)位置的值初始化為 0.0。關(guān)于數(shù)組的這種構(gòu)造方法請(qǐng)參考 創(chuàng)建一個(gè)帶有默認(rèn)值的數(shù)組。

你可以通過(guò)傳入合適的 rowcolumn 數(shù)值來(lái)構(gòu)造一個(gè)新的 Matrix 實(shí)例:

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

上例中創(chuàng)建了一個(gè)兩行兩列的 Matrix 實(shí)例。該 Matrix 實(shí)例的 grid 數(shù)組按照從左上到右下的閱讀順序?qū)⒕仃嚤馄交鎯?chǔ):

rowcolumn 的值傳入下標(biāo)來(lái)為矩陣設(shè)值,下標(biāo)的入?yún)⑹褂枚禾?hào)分隔:

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

上面兩條語(yǔ)句分別調(diào)用下標(biāo)的 setter 將矩陣右上角位置(即 row0column1 的位置)的值設(shè)置為 1.5,將矩陣左下角位置(即 row1、column0 的位置)的值設(shè)置為 3.2

Matrix 下標(biāo)的 getter 和 setter 中都含有斷言,用來(lái)檢查下標(biāo)入?yún)?rowcolumn 的值是否有效。為了方便進(jìn)行斷言,Matrix 包含了一個(gè)名為 indexIsValid(row:column:) 的便利方法,用來(lái)檢查入?yún)?rowcolumn 的值是否在矩陣范圍內(nèi):

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

斷言在下標(biāo)越界時(shí)觸發(fā):

let someValue = matrix[2, 2]
// 斷言將會(huì)觸發(fā),因?yàn)?[2, 2] 已經(jīng)超過(guò)了 matrix 的范圍

類型下標(biāo)

正如上節(jié)所述,實(shí)例下標(biāo)是在特定類型的一個(gè)實(shí)例上調(diào)用的下標(biāo)。你也可以定義一種在這個(gè)類型自身上調(diào)用的下標(biāo)。這種下標(biāo)被稱作類型下標(biāo)。你可以通過(guò)在 subscript 關(guān)鍵字之前寫下 static 關(guān)鍵字的方式來(lái)表示一個(gè)類型下標(biāo)。類類型可以使用 class 關(guān)鍵字來(lái)代替 static,它允許子類重寫父類中對(duì)那個(gè)下標(biāo)的實(shí)現(xiàn)。下面的例子展示了如何定義和調(diào)用一個(gè)類型下標(biāo):

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)
? 方法 繼承 ?
?