十二 下标swift
1、下标语法 数组
下标可让你经过实例名后加中括号内一个或多个数值的形式检索一个元素。语法和方法语法和属性语法相似,经过使用subscript关键定义,一个或多个输入参数以及一个返回值。不一样于实例方法的是,下标能够是可读写的或者只读的。这种行为经过一个getter和setter语句联通,就像是计算属性同样。app
subscript(index: Int) -> Int { get { return an appropriate subscript value here } set(newValue) { perform a suitable setting action here } }
newValue的类型和下标返回的类型同样。和计算属性同样,你能够选择不指定setter的参数,由于当你不指定的时候,默认参数newValue会被提供给setter。ide
和计算属性同样,只读下标能够不须要get关键词函数
下面是一个只读下标的实现,定义了一个TimesTable结构来表示一个整数的倍数表:工具
在这个例子中,实例TimesTable被建立为3倍数表,这是经过在初始化的时候为multiplier参数传入的数值3设置的。ui
注意:编码
倍数表是根据特定的数学规则设置的,因此不该该为threeTimeTable[someIndex]元素设置一个新值,因此TimesTable的下标定义为只读。spa
struct TimesTable { let multiplier: Int subscript(index: Int) -> Int { return multiplier * index } } let threeTimesTable = TimesTable(multiplier: 3) print("six times three is \(threeTimesTable[6])")
2、下标的使用orm
下标的具体含义由使用它时的上下文来肯定。下标主要用来做为集合,列表和序列的元素快捷方式。你能够自由的为你的类或者结构定义你所须要的下标。
好比说,Swift中字典类型实现的下标是设置和检索字典实例中的值。能够经过分别给出下标中的关键词和值来设置多个值,也能够经过下标来设置单个字典的值:
var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] numberOfLegs["bird"] = 2
上面的例子中定义了一个变量numberOfLegs,而后经过键值对初始化。numberOfLegs的类型是字典类型Dictionary<String, Int>。在字典建立以后,例子使用了下标赋值方法添加了一个类型为字符串的键”bird”和Int值2到字典中。
注意:
Swift中字典类型实现的键值对下标是可选类型。对于numberOfLges字典来讲,返回的值是Int?,也就是可选Int值。字典的这种使用可选类型下标的方式说明不是全部的键都有对应的值。一样也能够经过给键赋值nil来删除这个键。
3、下标选项
下标能够接收任意数量的参数,参数的类型也能够各异。下标还能够返回任何类型的值。下标可使用变量参数或者可变参数,可是不可以使用输入输出参数或者提供默认参数的值。
类或者结构能够根据须要实现各类下标方式,能够在须要的时候使用合适的下标经过中括号中的参数返回须要的值。这种多下标的定义被称做下标重载。
固然,最多见的下标用法是单个参数,也能够定义多个参数的下标。下面的例子演示了一个矩阵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(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, column: column), "Index out of range") grid[(row * columns) + column] = newValue } } }
矩阵Matrix提供了一个初始化方法,使用两个参数rows和columns,而后创建了一个数组来存储类型为Double的值rows*columns。每一个矩阵中的位置都被设置了一个初始值0.0。经过传递初始值0.0和数组长度给数组初始化方法完成上述操做
你能够传递两个参数row和column来完成Matrix的初始化:
var matrix = Matrix(rows: 2, columns: 2)
矩阵中的值能够经过使用包含row和column以及逗号的下标来设置:
matrix[0, 1] = 1.5 matrix[1, 0] = 3.2
矩阵下标的getter和setter方法都包括了一个断言语句来检查下标row和column是否有效。经过indexIsValid方法来判断row和column是否在矩阵的范围内:
若是访问的矩阵越界的时候,断言就会被触发:
let someValue = matrix[2, 2]
控制台打印:assertion failed: Index out of range: file <EXPR>, line 61
十三 继承
1、定义一个基类
任何一个不继承于其它类的类被称做基类
注意:Swift的类不是从一个全局基类继承而来。在你编写代码的时,只要是在类的定义中没有继承自父类的类都是基类。
下面的例子定义了一个叫Vehicle的基类。基类包含两个全部交通工具通用的属性numberOfWheels和maxPassengers。这两个属性被一个叫description的方法使用,经过返回一个String描述来做为这个交通工具的特征:
class Vehicle { var numberOfWheels: Int var maxPassengers: Int func description() -> String { return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers" } init() { numberOfWheels = 0 maxPassengers = 1 } }
这个交通工具类Vehicle还定义了一个构造函数来设置它的属性。
经过构造函数能够建立一个类型的实例。尽管构造函数不是方法,可是它们在编码的时候使用了很是类似的语法。构造函数经过确保全部实例的属性都是有效的来建立一个新的实例。
使用构造函数语法TypeName和空的两个小括号来完成一个Vehicle实例的建立:
let someVehicle = Vehicle()
Vehicle的构造函数为属性设置了一些初始值(numberOfWheels = 0 而后 maxPassengers = 1)。
Vehicle类定义的是一个通用的交通工具特性,它自己没有太多意义,因此就须要冲定义它的一些属性或者方法来让它具备实际的意义。
2、产生子类
产生子类就是根据一个已有的类产生新类的过程。子类继承了父类的一些能够修改的特性。还能够为子类添加一些新的特性。
为了代表一个类是继承自一个父类,须要将父类的名称写在子类的后面,而且用冒号分隔
定义一个新的类叫Bicycle,它继承了Vehicle的特性:
class Bicycle: Vehicle { override init() { super.init() numberOfWheels = 2 } }
不只属性是继承于Vehicle的,Bicycle还继承了父类的方法。若是你建立一个实例,而后调用了已经继承的description方法,能够获得该交通工具的描述而且看到它的属性已经被修改:
let bicycle = Bicycle() print("Bicycle: \(bicycle.description())")
注意:子类只可以在构造的时候修改变量的属性,不能修改常量的属性。
3、重写方法
子类能够提供由父类继承来的实例方法,类方法,实例属性或者下标的个性化实现。这个特性被称为重写。
重写一个由继承而来的方法须要在方法定义前标注override关键词。经过这样的操做能够确保你所要修改的这个方法确实是继承而来的,而不会出现重写错误。错误的重写会形成一些不可预知的错误,因此若是若是不标记override关键词的话,就会被在代码编译时报错。
override关键词还可以让Swift编译器检查该类的父类是否有相符的方法,以确保你的重写是可用的,正确的。
访问父类方法,属性和下标
当在重写子类继承自父类的方法,属性或者下标的时候,须要用到一部分父类已有的实现。好比你能够重定义已知的一个实现或者在继承的变量中存储一个修改的值。
适当的时候,能够经过使用super前缀来访问父类的方法,属性或者下标
复写方法
你能够在你的子类中实现定制的继承于父类的实例方法或者类方法。
下面的例子演示的就是一个叫Car的Vehicle子类,重写了继承自Vehicle的description方法。
class Car: Vehicle { var speed: Double = 0.0 override init() { super.init() maxPassengers = 5 numberOfWheels = 4 } override func description() -> String { return super.description() + "; " + "traveling at \(speed) mph" } }
Car中定义了一个新的Double类型的存储属性speed。这个属性默认值是0.0,意思是每小时0英里。Car还有一个自定义的构造函数,设置了最大乘客数为5,轮子数量是4.
Car重写了继承的description方法,并在方法名description前标注了override关键词。
在description中并无给出了一个全新的描述实现,仍是经过super.description使用了Vehicle提供的部分描述语句,而后加上了本身定义的一些属性,如当前速度。
let car = Car() print("Car: \(car.description())")
复写属性
你还能够提供继承自父类的实例属性或者类属性的个性化getter和setter方法,或者是添加属性观察者来实现重写的属性能够观察到继承属性的变更。
重写属性的Getters和Setters
无论在源类中继承的这个属性是存储属性仍是计算属性,你均可以提供一个定制的getter或者setter方法来重写这个继承属性。子类通常不会知道这个继承的属性原本是存储属性仍是计算属性,可是它知道这个属性有特定的名字和类型。在重写的时候须要指明属性的类型和名字,好让编译器能够检查你的重写是否与父类的属性相符。
你能够将一个只读的属性经过getter和setter继承为可读写的,可是反之不可。
注意:若是你为一个重写属性提供了setter方法,那么也须要提供getter方法。若是你不想在getter中修改继承的属性的值,能够在getter中使用super.someProperty便可,在下面SpeedLimitedCar例子中也是这样。
下面的例子定义了一个新类SpeedLimitedCar,是Car的一个子类。这个类表示一个显示在40码一下的车辆。经过重写继承的speed属性来实现:
class SpeedLimitedCar: Car { override var speed: Double { get { return super.speed } set { super.speed = min(newValue, 40.0) } } }
重写属性观察者
你可使用属性重写为继承的属性添加观察者。这种作法可让你不管这个属性以前是如何实现的,在继承的这个属性变化的时候都能获得提醒。
注意:不能为继承的常量存储属性或者是只读计算属性添加观察者。这些属性值是不能被修改的,所以不适合在重写实现时添加willSet或者didSet方法。
注意:不能同时定义重写setter和重写属性观察者,若是想要观察属性值的变化,而且又为该属性给出了定制的setter,那只须要在setter中直接得到属性值的变化就好了。
下面的代码演示的是一个新类AutomaticCar,也是Car的一个子类。这个类代表一个拥有自动变速箱的汽车,能够根据如今的速度自动选择档位,并在description中输出当前档位:
class AutomaticCar: Car { var gear = 1 override var speed: Double { didSet { gear = Int(speed / 10.0) + 1 } } override func description() -> String { return super.description() + " in gear \(gear)" } }
这样就能够实现,每次你设置speed的值的时候,didSet方法都会被调用,来看档位是否须要变化。gear是由speed除以10加1计算得来,因此当速度为35的时候,gear档位为4:
let car1:AutomaticCar = AutomaticCar() car1.speed = 35 car1.description()
4、禁止重写
你能够经过标记final关键词来禁止重写一个类的方法,属性或者下标。在定义的关键词前面标注final属性便可。
在子类中任未尝试重写父类的final方法,属性或者下标的行为都会在编译时报错。一样在扩展中为类添加的方法,属性或者下标也能够被标记为final。
还能够在类关键词class前使用final标记一整个类为final(final class)。任何子类尝试继承这个父类时都会在编译时报错。