对于同一个目标可以定义多个附属脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。
译者:这里附属脚本重载在本小节中原文并没有任何演示。
附属脚本语法
附属脚本允许你通过在实例后面的方括号中传入一个或者多个的索引值来对实例进行访问和赋值。语法类似于实例方法和计算型属性的混合。与定义实例方法类似,定义附属脚本使用subscript关键字,显式声明入参(一个或多个)和返回类型。与实例方法不同的是附属脚本可以设定为读写或只读。这种方式又有点像计算型属性的getter和setter:
- subscript(index: Int) -> Int {
- get {
- // 返回与入参匹配的Int类型的值
- }
- set(newValue) {
- // 执行赋值操作
- }
- }
与只读计算型属性一样,可以直接将原本应该写在get代码块中的代码写在subscript中:
- subscript(index: Int) -> Int {
- // 返回与入参匹配的Int类型的值
- }
- struct TimesTable {
- let multiplier: Int
- subscript(index: Int) -> Int {
- return multiplier * index
- }
- }
- let threeTimesTable = TimesTable(multiplier: 3)
- println("3的6倍是\(threeTimesTable[6])")
- // 输出 "3的6倍是18"
你可以通过附属脚本来来得到结果,比如threeTimesTable[6]。这句话访问了threeTimesTable的第六个元素,返回18或者6的3倍。
注意:TimesTable例子是基于一个固定的数学公式。它并不适合开放写权限来对threeTimesTable[someIndex]进行赋值操作,这也是为什么附属脚本只定义为只读的原因。
附属脚本用法
根据使用场景不同附属脚本也具有不同的含义。通常附属脚本是用来访问集合(collection),列表(list)或序列(sequence)中元素的快捷方式。你可以在你自己特定的类或结构体中自由的实现附属脚本来提供合适的功能。例如,Swift 的字典(Dictionary)实现了通过附属脚本来对其实例中存放的值进行存取操作。在附属脚本中使用和字典索引相同类型的值,并且把一个字典值类型的值赋值给这个附属脚本来为字典设值:
- var numberOfLegs = ["spider": 8,"ant": 6,"cat": 4]
- numberOfLegs["bird"] = 2
注意:Swift 中字典的附属脚本实现中,在get部分返回值是Int?,上例中的numberOfLegs字典通过下边返回的是一个Int?或者说“可选的int”,不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是nil;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为nil即可。
附属脚本选项
附属脚本允许任意数量的入参索引,并且每个入参类型也没有限制。附属脚本的返回值也可以是任何类型。附属脚本可以使用变量参数和可变参数,但使用写入读出(in-out)参数或给参数设置默认值都是不允许的。一个类或结构体可以根据自身需要提供多个附属脚本实现,在定义附属脚本时通过入参个类型进行区分,使用附属脚本时会自动匹配合适的附属脚本实现运行,这就是附属脚本的重载。
一个附属脚本入参是最常见的情况,但只要有合适的场景也可以定义多个附属脚本入参。如下例定义了一个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(count: rows * columns,repeatedValue: 0.0)
- }
- func indexIsValidForRow(row: Int,column: Int) -> Bool {
- return row >= 0 && row < rows && column >= 0 && column < columns
- }
- subscript(row: Int,column: Int) -> Double {
- get {
- assert(indexIsValidForRow(row,column: column),"Index out of range")
- return grid[(row * columns) + column]
- }
- set {
- assert(indexIsValidForRow(row,"Index out of range")
- grid[(row * columns) + columns] = newValue
- }
- }
- }
你可以通过传入合适的row和column的数量来构造一个新的Matrix实例:
- var matrix = Matrix(rows: 2,columns: 2)
将值赋给带有row和column附属脚本的matrix实例表达式可以完成赋值操作,附属脚本入参使用逗号分割
- matrix[0,1] = 1.5
- matrix[1,0] = 3.2
Matrix附属脚本的getter和setter中同时调用了附属脚本入参的row和column是否有效的判断。为了方便进行断言,Matrix包含了一个名为indexIsValid的成员方法,用来确认入参的row或column值是否会造成数组越界:
- func indexIsValidForRow(row: Int,column: Int) -> Bool {
- return row >= 0 && row < rows && column >= 0 && column < columns
- }
- let someValue = matrix[2,2]
- // 断言将会触发,因为 [2,2] 已经超过了matrix的最大长度