05 來,自定義一個swift的subscript

本文參考原文為Implementing Custom Subscripts in Swift,歡迎閱讀原文。

下標(biāo)是一種強大的語言功能,如果使用得當(dāng),可以顯著提高代碼的調(diào)用的便利性和可讀性。在本教程中,我們將一起在playgroud中,通過構(gòu)建一個基本跳棋游戲來探索下標(biāo)。通過使用下標(biāo),你可以非常容易的在棋盤上移動一個棋子。

1、開始行動

struct Checkerboard {
 
  enum Square: String {
    case Empty = "\u{25AA}\u{fe0f}" // Black square
    case Red = "\u{1f534}"          // Red piece
    case White = "\u{26AA}\u{fe0f}" // White piece
  }
 
  typealias Coordinate = (x: Int, y: Int)
 
  private var squares: [[Square]] = [
    [ .Empty, .Red,   .Empty, .Red,   .Empty, .Red,   .Empty, .Red   ],
    [ .Red,   .Empty, .Red,   .Empty, .Red,   .Empty, .Red,   .Empty ],
    [ .Empty, .Red,   .Empty, .Red,   .Empty, .Red,   .Empty, .Red   ],
    [ .Empty, .Empty, .Empty, .Empty, .Empty, .Empty, .Empty, .Empty ],
    [ .Empty, .Empty, .Empty, .Empty, .Empty, .Empty, .Empty, .Empty ],
    [ .White, .Empty, .White, .Empty, .White, .Empty, .White, .Empty ],
    [ .Empty, .White, .Empty, .White, .Empty, .White, .Empty, .White ],
    [ .White, .Empty, .White, .Empty, .White, .Empty, .White, .Empty ]
  ]
}
 
extension Checkerboard: CustomStringConvertible {
  var description: String {
    return squares.map { row in row.map { $0.rawValue }.joinWithSeparator("") }
                  .joinWithSeparator("\n") + "\n"
  }
}

我們在Checkerboard中定義了三個元素:

  • Square: 代表棋盤上的一個格子,.Empty代表一個空格子,.Red和.White分別代表的是紅色和白色的棋子。

  • Coordinate: typealias一個有兩個Int類型數(shù)據(jù)構(gòu)成的Tuple,代表棋盤上一個格子的坐標(biāo)點。
    我們使用Coordinate來訪問棋盤上的一個位置。

  • squares: 是一個二維數(shù)組,用來表示棋盤上的每一個格子的狀態(tài)。

我們現(xiàn)在在playground下面加入以下兩行代碼,

var checkerboard = Checkerboard()
print(checkerboard)

我們初始化棋盤并打印它的description屬性,我們會看到如下的輸出。

2、將棋子移動到棋盤對應(yīng)的位置

我們每次移動棋子,其實是相當(dāng)于改變棋盤上面格子(square)的狀態(tài),將square的狀態(tài)設(shè)置為.Empty、.Red、.White三種狀態(tài)之一。square的狀態(tài)保存在squares中,但是根據(jù)面向?qū)ο蟮脑O(shè)計原則,我們應(yīng)該盡可能的避免向外部暴露我們內(nèi)實現(xiàn)的細節(jié),這樣才可以為我們程序修改升級保留足夠的空間,所以我們將squares數(shù)組設(shè)置為private。所以我們這個時候要增加兩個方法,一個用來訪問square當(dāng)前的狀態(tài),一個用來修改square的狀態(tài)。

我們在 Checkerboard 內(nèi)部增加如下兩個方法。

func pieceAt(coordinate: Coordinate) -> Square {
  return squares[coordinate.y][coordinate.x]
}
 
mutating func setPieceAt(coordinate: Coordinate, to newValue: Square) {
  squares[coordinate.y][coordinate.x] = newValue
}

我們需要注意的是,我們這兩方法的參數(shù)使用的Coordinate類型,而不是數(shù)組的坐標(biāo),這樣可以避免暴露內(nèi)部的實現(xiàn),因為如果我們參數(shù)使用數(shù)組的坐標(biāo),但未來某一天我們不再使用數(shù)組作為定義棋盤的數(shù)據(jù)結(jié)構(gòu),那么我們這兩個方法就難以和未來的實現(xiàn)相兼容了。

現(xiàn)在我們可以更新square的狀態(tài)了,但是我們新增的這個兩個方法先得有些丑陋,他們完全不像是一個swift自有的內(nèi)容,而是被我們從外部生硬的塞進來的。

3、定義下標(biāo)(Subscripts)

我們現(xiàn)在來調(diào)整一下我們的代碼,我們是否可以使用計算屬性來重新定義這兩個方法呢?很明顯,是不行的,因為我們的方法需要參數(shù),但計算屬性是不可以有參數(shù)。但是我們可以使用下標(biāo)(Subscripts).

subscript(parameterList) -> ReturnType {
  get {
    // return someValue of ReturnType
  }
 
  set (newValue) {
    // set someValue of ReturnType to newValue
  }
}

下標(biāo)定義的語法同時具有函數(shù)定義和計算屬性定義的語法特征。

  • 首先它像一個函數(shù)的定義,有參數(shù)列表,有返回值只是用subscript關(guān)鍵字代替了func關(guān)鍵字

  • 它的方法體內(nèi)更像一個計算屬性的定義,包括getter和setter方法

我們用下面的代碼替換掉pieceAt和setPieceAt兩個方法。

subscript(coordinate: Coordinate) -> Square {
  get {
    return squares[coordinate.y][coordinate.x]
  }
  set {
    squares[coordinate.y][coordinate.x] = newValue
  }
}

然后我們在playground的末尾增加下面的代碼,將(3,2)點的坐標(biāo)設(shè)置為.White狀態(tài)。

let coordinate = (x: 3, y: 2)
print(checkerboard[coordinate])
checkerboard[coordinate] = .White
print(checkerboard)

我們會得到如下的輸出結(jié)果。

4、比較下標(biāo)、屬性和函數(shù)

下標(biāo)在一下幾個方面很像計算屬性:

  • 它也是由 getter 和 setter方法構(gòu)成的。
  • setter方法是可選的,也就是說它既可以是可讀寫的,也可以是只讀的。
  • 一個只讀的下標(biāo),不需要顯示的設(shè)置get和set狀態(tài)
  • 對于setter方法,它有一個默認的newValue參數(shù),類型與返回值類型相同
  • 盡量使下標(biāo)操作的時間復(fù)雜度為O(1)

下標(biāo)與計算屬相最大的不同在于,下標(biāo)沒有一個屬性名,和重載操作符類似,下標(biāo)可以覆蓋swift自己的中括號[].

下標(biāo)和函數(shù)的相似之處在于有參數(shù)列表,有返回值。不同點在于:

  • 下標(biāo)沒有默認的外部參數(shù)名,如果你需要使用外部參數(shù)名,那么需要顯示的指定;
  • 下標(biāo)不能使用inout關(guān)鍵字,也不能使用默認參數(shù),但可以使用可變長參數(shù);
  • 下標(biāo)不能throw錯誤,也就是說下標(biāo)的 getter方法必須通過返回值來表示錯誤setter方法既不能拋出錯誤也不能返回錯誤。

5、增加第二個下標(biāo)

下標(biāo)可以被重載,因此我們可以定義多個下標(biāo),只要他們有不同的參數(shù)列表或返回值就可以了。我們在Checkerboard里面新增下面的代碼。

subscript(x: Int, y: Int) -> Square {
  get {
    return self[(x: x, y: y)]
  }
  set {
    self[(x: x, y: y)] = newValue
  }
}

我們新增的這個方法,使用二維數(shù)組的坐標(biāo),作為參數(shù)。

現(xiàn)在你已經(jīng)掌握了swift的下標(biāo)了,嘗試尋找機會在你自己的代碼里面定義它吧,它一定會使用你的代碼更具可讀性。如果內(nèi)容中有任何錯誤,請和我聯(lián)系,謝謝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 基礎(chǔ)部分(The Basics) 當(dāng)推斷浮點數(shù)的類型時,Swift 總是會選擇Double而不是Float。 結(jié)合...
    gamper閱讀 1,496評論 0 7
  • 123.繼承 一個類可以從另外一個類繼承方法,屬性和其他特征。當(dāng)一個類繼承另外一個類時, 繼承類叫子類, 被繼承的...
    無灃閱讀 1,493評論 2 4
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 4,194評論 1 10
  • myr-6 羊絨大衣在冬天是不錯的保暖服裝,但是我們往往不知道怎樣正確地保養(yǎng)和護理它,因為羊絨是個比較脆弱的纖維構(gòu)...
    6_myr閱讀 808評論 0 0
  • 問題 〈鴻鸞禧〉、〈琉璃瓦〉兩篇作品塑造了兩位父親,婁囂伯以及姚源甫,試問作家為何要在女性婚嫁主題小說內(nèi)安排這兩位...
    耿涌閱讀 221評論 0 0

友情鏈接更多精彩內(nèi)容