Swift的继承与其他语言并无两样,本文主要把重写进行了说明,主要是方法/下标重写、属性重写、属性观察器重写,其实他们的本质也就是方法重写。
主要内容:
- 方法/下标重写
- 属性重写
- 属性观察器重写
1、继承认识
继承概念没区别,都是父类子类的关系,也没必要赘言了
这里只看下内存结构
代码:
/*
1、内存结构
*/
func test1() {
class Animal {
var age = 0
}
class Dog : Animal {
var weight = 0
}
class ErHa : Dog {
var iq = 0
}
//父类
let a = Animal()
a.age = 10
// 32
print(Mems.size(ofRef: a))
/*
0x00000001000073e0
0x0000000000000002
0x000000000000000a
0x0000000000000000
*/
print(Mems.memStr(ofRef: a))
//子类
let d = Dog()
d.age = 10
d.weight = 20
// 32
print(Mems.size(ofRef: d))
/*
0x0000000100007490
0x0000000000000002
0x000000000000000a
0x0000000000000014
*/
print(Mems.memStr(ofRef: d))
//孙子类
let e = ErHa()
e.age = 10
e.weight = 20
e.iq = 30
// 48
print(Mems.size(ofRef: e))
/*
0x0000000100007560
0x0000000000000002
0x000000000000000a
0x0000000000000014
0x000000000000001e
0x0000000000000000
*/
print(Mems.memStr(ofRef: e))
}
test1()
说明:
- 父类继承过来的存储属性是放在前面的
- 子类继承父类的存储属性是拷贝到子类中的
2、重写
子类可以重写父类的属性、下标、方法,重写必须加上override关键字(required不需要),需要注意的是不管是方法、下标、属性、属性观察器,所有的重写本质都是函数重写
2.1 方法/下标
下标其实就是方法,所以放到一起写
2.1.1 实例方法
代码:
/*
2、下标、方法重写
*/
func test2() {
class Animal {
func speak() {
print("Animal speak")
}
subscript(index: Int) -> Int {
return index
}
}
class Cat : Animal {
//方法重写
override func speak() {
super.speak()
print("Cat speak")
}
//下标重写
override subscript(index: Int) -> Int {
return super[index] + 1
}
}
let cat: Animal = Cat()
cat.speak()
print(cat[1])
}
test2()
说明:
- 下标和方法只要在前边加上override就声明被重写了
2.1.2 类型方法
代码:
/*
2、下标、方法重写
*/
func test2() {
class Animal {
class func speak() {
print("Animal speak")
}
class subscript(index: Int) -> Int {
return index
}
}
class Cat : Animal {
//方法重写
override class func speak() {
super.speak()
print("Cat speak")
}
//下标重写
override class subscript(index: Int) -> Int {
return super[index] + 1
}
}
Cat.speak()
print(Cat(2))
}
test2()
说明:
- 类型方法也是一样,只是在调用时需要通过类型调用
- 被class修饰的类型方法、下标,允许被子类重写
- 被static修饰的类型方法、下标,不允许被子类重写
- 注意重写的方法/下标可以是static
2.2 属性
属性本身是没有重写的,只有继承,但是在Swift中有计算属性和属性观察器这两种东西,他们本质上也是函数,所以这两种也可以重写
属性重写的种类:
- 计算属性重写为计算属性;
- 存储属性重写为计算属性;
- 存储属性重写成带有属性观察器的存储属性
代码:
/*
3、属性重写
*/
func test3() {
class Circle {
var radius: Int = 0
var diameter: Int {
set {
print("Circle setDiameter")
radius = newValue / 2
}
get {
print("Circle getDiameter")
return radius * 2
}
}
}
class SubCircle : Circle {
override var radius: Int {
set {
print("SubCircle setradius")
super.radius = newValue > 0 ? newValue : 0
}
get {
print("SubCircle getradius")
return super.radius
}
}
override var diameter: Int {
set {
print("SubCircle setDiameter")
super.diameter = newValue > 0 ? newValue : 0
}
get {
print("SubCircle getDiameter")
return super.diameter
}
}
}
let circle = SubCircle()
//SubCircle setradius
circle.radius = 6
/*
SubCircle getDiameter
Circle getDiameter
SubCircle getradius
12
*/
print(circle.diameter)
//SubCircle setDiameter
circle.diameter = 20
/*
SubCircle setDiameter
Circle setDiameter
SubCircle setradius
SubCircle getradius
10
*/
print(circle.radius)
}
test3()
说明:
- 仅仅只是普通的重写,没什么特殊的
- 只是需要注意在调用circle.diameter时,调用一下父类的getDiameter方法,因为在子类的getDiameter方法中会调用super的diameter
注意:
- 子类可以将存储属性和计算属性重写为计算属性
- 子类不可以将父类属性重写成存储属性
- 存储属性只能继承
- 但是如果子类增加属性观察器,也就可以重写
- 子类不能将父类的存储属性重写为存储属性,因为子类本身就会有父类的存储属性,所以没必要重写
- 只能重写var属性,不能重写let属性,这个很正常,重写就是为了子类修改实现
- 重写时,属性名、类型要一致
- 子类重写后的属性权限不能小于父类属性的权限
- 被Class修饰的计算类型属性,可以被子类重写
- 被static修饰的类型属性(存储、计算),不可以被子类重写
2.3 属性观察器
可以在子类中为父类属性增加属性观察器,此时也需要使用override修饰
代码:
/*
4、属性观察器重写
*/
func test4() {
class Circle {
var radius: Int = 1
}
class SubCircle : Circle {
override var radius: Int {
willSet {
print("SubCircle willSetRadius",newValue)
}
didSet {
print("SubCircle didSetRadius",oldValue,radius)
}
}
}
let circle = SubCircle()
/*
SubCircle willSetRadius 10
SubCircle didSetRadius 1 10
*/
circle.radius = 10
}
test4()
说明:
- let属性、只读计算属性不能增加属性观察器
- 如果是给父类的存储属性增加属性观察器,那么子类中只会继承父类的存储属性,没有变成计算属性。仅仅是增加了属性观察器
- 如果父类的存储属性也有属性观察器,子类也是可以重写的
- 也可以给父类的计算属性重写属性观察器
3、总结
注意:
- 值类型(枚举、结构体)不支持继承,只有类支持继承
- Swift中的类没有统一的基类,比如NSObject
- 没有继承任何父类的类是基类,但其实它 是有一个父类的,叫_SwiftObject
- 被final修饰的方法、下标、属性禁止被重写,被final修饰的类,禁止被继承
总结:
- 重写除了一般的方法重写,还有下标重写、属性重写、属性观察器重写
- 其中下标、属性观察器、计算属性的重写本质上都是属于方法重写
- 只是注意一点可以将存储属性重写为计算属性