继承(Inheritance)
- 值类型(枚举、结构体)不支持继承,只有类支持继承
- 没有父类的类,称为:基类
- Swift并没有像OC、Java那样的规定:任何类最终都要继承自某个基类
- 子类可以重写父类的下标、方法、属性,重写必须加上override关键字
内存结构
Dog继承自Animal,也就拥有了age属性, Dog实际占用的内存空间是32个字节,其中指针类型8个字节,引用计数8个字节,age8个字节,weight8个字节
ErHa继承Dog,也就拥有了age,weight和iq三个属性,ErHa实际占用的内存空间是48个字节,其中指针类型8个字节,引用计数8个字节,age8个字节,weight8个字,iq8个字节,因为要内存对齐,都要是16的倍数,所以还有8个空字节
重写实例方法、下标
下面的代码属于父类指针指向子类对象,类似于OC的多态,anim调用的方法还是Cat的方法
重写类型方法、下标
- 被class修饰的类型方法、下标,允许被子类重写
- 被static修饰的类型方法、下标,不允许被子类重写
但是如果此时speak方法写成 static类型,它的子类whiteCar是不能重写speak方法的,会报错,如下图:
重写属性注意点
- 子类可以将父类的属性(存储、计算)重写为计算属性
- 子类不可以将父类属性重写为存储属性
- 只能重写var属性,不能重写let属性(因为子类会涉及到修改属性,let不允许修改,所以不能重写let)
- 重写时,属性名、类型要一致
- 子类重写后的属性权限 不能小于 父类属性的权限
- 如果父类属性是只读的,那么子类重写后的属性可以是只读的、也可以是可读写的
- 如果父类属性是可读写的,那么子类重写后的属性也必须是可读写的
重写实例属性
重点提示:虽然SubCircle里的radius写成了计算属性,但是subCircle仍然需要开辟8个字节的内存空间存储radius,总之一句话,从父类继承过来的存储属性,都有存储空间,子类通过super关键字访问存储空间,如果你不使用super,如下图:
这种写法会造成死循环:因为get里的radius可以看作self.radius,这会造成在SubCircle里不断调用它的radius,造成死循环。
重写类型属性
- 被class修饰的计算类型属性,可以被子类重写
- 被static修饰的类型属性(存储、计算),不可以被子类重写
- class不可以修饰存储类型属性(static可以修饰存储类型属性),所以更不可能被子类重写,如下图
属性观察器
可以在子类中为父类属性(除了只读计算属性、let属性)增加属性观察器
- 为存储属性添加属性观察器
- 父类和子类同时有属性观察器
注意:这里走circlr.radius = 10时会先走Circle didsetRadius,再走SubCircle didSetRadius,因为是在父类赋值,所以先走父类的完成赋值。
- 给计算属性(包括类计算属性和实例计算属性)添加属性观察器,要注意,正常情况下在一个类中不能给计算属性加属性观察器(set,get与willset,didSet不能共存),但是可以通过子类给父类的计算属性添加属性观察器
- 类计算属性
2. 实例计算属性
注意:从上图的输出顺序可以看到,会先走Circle getRadius(也就是Circle的get),这是因为会首先获得Circle的oldValue的值,然后走SubCircle的WillSet,接下来走Circle的set,在最后走入SubCircle的didSet的时候,会首先获得radius的值,所以会先走Circle的getRadius,最后走SubCircle didSetRadius。
final
- 被final修饰的方法、下标、属性,禁止被重写,会报错如下图
- 被final修饰的类,禁止被继承,会报错如下图