下标
类、结构体和枚举可以使用下标,用来方便访问一个集合、列表或序列的成员。可以使用下标根据索引设置和获取值,而不需要单独的方法。举例来说,你可以使用someArray[index]来访问一个数组的实例中的元素,也可以使用someDictionary[key]来访问一个字典中的元素。
可以定义单一类型的多个脚本,根据你传递给脚本的索引值的类型会自动选择合适的重载脚本。脚本并不限制只能有一维,可以多个参数,以适应定制的需要。
下标语法
下标可以做到:实例名称之后的方括号中写一个或多个值来查询类型实例。它的语法类型实例方法语法和计算属性语法。使用subscript关键字定义下标,然后指定一个或多余一个的参数和返回类型,就像实例方法一样。不同于实例方法,下标可以是可读写的也可以是只读的。就像计算属性一样,这种限制是通过getter和setter实现的:
subscript(index: Int) -> Int { get { // return an appropriate subscript value here } set(newValue) { // perform a suitable setting action here } }
newValue 的类型和下标返回值的类型相同。和计算属性一样,你可以选择不指定setter的参数((newValue))。没有指定参数的时候默认参数newValue生效。
和只读计算属性一样,可以把只读脚本中的get关键字丢掉:
subscript(index: Int) -> Int { // return an appropriate subscript value here }
这里有一个只读下标的示例,它定义了一个叫做TimesTable的结构体表示一个整数的n倍数表:
truct TimesTable { let multiplier: Int subscript(index: Int) -> Int { return multiplier * index } } let threeTimesTable = TimesTable(multiplier: 3) println("six times three is \(threeTimesTable[6])") // prints "six times three is 18"
这个例子中,一个新的TimesTable实例被创建,用来表现一个整数的三倍数表。具体的,是通过传递一个3给结构体的初始化方法,初始化multiplier为3.
可以通过下标查询threeTimesTable实例,就像上面的threeTimesTable[6]。这个语句请求了three-times-table中的第六个元素,返回了18,也就是6的3倍。
NOTE
一个n倍数表依据的是数学规则。不会给threeTimesTable[someIndex]赋予一个新值,所以TimesTable的这个下标是一个只读下标。
下标用法
下标准确意义要依赖其使用的上下文。下标的典型用法是简洁的访问集合、列表或者序列的成员。也可以为了类或者结构体的特殊功能,用适当的方式实现下标。
举例来说,Swift的字典类型实现了一个下标来实现对字典类型实例中的值的读取和赋值。你可以在方括号中提供一个字典键值来设置一个字典内容,也可以将字典值赋给下标:
var numberOfLegs = ["spider": 8,"ant": 6,"cat": 4] numberOfLegs["bird"] = 2
上面定义了一个变量叫做numberOfLegs,然后使用了包含三个键值对的字典的字面语句对其初始化。字典numberOfLegs是一个[String:Int]类型的。创建完这个字典后,例子采用下标分配并添加了一个键为“bird”,值为整型2的内容。
更多的关于字典下标的内容,参见 Accessing and Modifying a Dictionary。
NOTE
Swift的字典类型实现了它的key-vlaue下标,通过下标得到一个可选类型。比如上面的numberOfLegs字典,它的下标返回一个类型是Int?(或者可选int)的值。字典类型使用可选类型下标来模仿不是所有的键都会有一个对应的值,提供了一种处理可能对应的键为nil的途径。
Subscript Options
下标可以使用人一个数量的参数,而且这些参数可以是任意类型的。下标可以返回任意类型。下标可以使用变量参数,也可以使用可变参数,但是不能使用in-out参数或者提供参数默认值。
一个类好哦这结构体可以有多个下标实现,只要有必要,会根据在方括号中的值类型选择合适的下标实现。这种多下标的情况叫做下标重载(subscript overloading)。
尽管多数下标采用单一参数,但是你也可以定义多个参数的。下面的例子定义了一个矩阵结构体,表示了一个二维Double矩阵。它的下标就采用了两个整型做参数:
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) + column] = newValue } } }
Matrix提供了带两个参数(rows 和columns)的构造方法,创建了一个足够容纳rows*columns个值的Double数组。其中的每个位置都被给另一个初始值0.0.为了达到这个目的,数组的大小和每个位置的初始值被作为初始化参数传递进来。关于初始化方法的更多信息,参见Creating and Initializing an Array。
可以根据行列的数目来创建一个新的Matrix实例:
var matrix = Matrix(rows: 2,columns: 2)
上面的例子创建了一个Matrix实例,它有两行两列。Matrix实例的grid数组是一个平的矩阵,读取它要从左上到右下:
传递行和列的值(用逗号分割他们)给下标,可以对矩阵中的值进行设置:
matrix[0,1] = 1.5 matrix[1,0] = 3.2
这两句调用了下标的setter给矩阵右上角的位置(row 是0,column是1)设置了值1.5,给左下角的位置(row是1,column是0)设置了值3.2:
Matrix下标的getter和setter都含有一个检查下标的row 和column是否合法的断言。为了支持这个断言,Matrix提供了一个方便调用的方法叫做indexIsValidForRow(_:column:),这个方法检查了请求的row和column是否在矩阵的范围内:
func indexIsValidForRow(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