Swift-继承

继承可以理解为一个类获取了另外一个类的方法和属性。当一个类继承其他类时,继承类叫子类,被继承类叫超类(或父类)。在swift中,类可以调用和访问超类的方法、属性和下表脚本,并且可以重写它们,也可以为类中继承来的属性添加属性观察器。

继承是面向对象最显著的一个特性,继承是从已有的类中派生出新的类,新的类能够继承已有类的属性和方法,并扩展新的能力

术语:基类(父类、超类),派生类(子类、继承类)

语法

1
2
3
class 子类: 父类{
// TODO
}

优缺点

  • 优点:代码重用
  • 缺点:增加程序耦合度,父类改变会影响子类
  • 注意点:Swift和OC一样没有多继承

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Man {
var name:String = "daisuke"
var age:Int = 26
func sleep() {
print("父类要睡觉了")
}
}

class Son: Man {
var weight:Double = 100
func run(){
print("子类要跑步了")
}
func fly(){
print("访问父类的属性,name:\(name), age:\(age)")
}
}


class TestViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

let man = Man()
man.sleep()
// 父类不可以使用子类的方法
// man.fly()
// 父类不可以使用子类的属性
// man.weight

let son = Son()
// 子类使用父类的方法
son.sleep()
son.fly()
son.name = "daisuke--haha"
print(son.name)
}
}

super关键字

子类(派生类)可以通过super关键字来引用父类的属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Man {
var name:String = "daisuke"
var age:Int = 26
func sleep() {
print("父类要睡觉了")
}
func run(){
print("父类要跑步了")
}
}

class Son1: Man {
var weight:Double = 100
func fly(){
print("子类要飞起来了")
// 子类可以继承父类的属性
print("访问父类的属性,name:\(super.name), age:\(super.age)")
}
func flyAndRun() {
fly()
super.run()
// 如果没有写super,那么会在当前类中查找run()方法,如果找不到再去父类中查找
// 如果写了super,会直接去父类中查找
}
}


class TestViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

let son1 = Son1()
son1.flyAndRun()
}
}

override重写

override关键字主要为了明确表示重写父类方法,所以如果重写父类方法,必须加上override关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Son2: Man {

override func sleep() {
// sleep() // 不能这样写,会导致递归,死循环
super.sleep()
print("子类重写了父类的睡觉方法")
}

func eat() {
print("子类吃饭")
}

func eatAndSleep() {
eat()
sleep()
}
}

class TestViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

let son2 = Son2()
son2.sleep()
// son2.eatAndSleep()
// 通过子类调用,有哦县调用子类重写的方法
}
}

重写属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Man1 {
var name:String = "daisuke" // 存储属性
var age:Int { // 计算属性
get{
return 26
}
set{
print("父类新的年龄是:\(newValue)")
}
}
func sleep(){
print("父类睡觉")
}
}

class Son3: Man1 {
/*
可以将父类的存储属性重写为计算属性,但不可以将父类的存储属性又重写为存储属性,因为这样没有意义
ovverride var name:String = "newName"
*/

override var name: String{
get{
return "override name"
}
set{
print("override new name = \(newValue)")
}
}

// 可以将父类的计算属性重写为计算属性,同样不可以重写为存储属性
override var age: Int{ // 计算属性
get{
return 30
}
set{
print("override new age = \(newValue)")
}
}
}

class TestViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

let son3 = Son3()
son3.name = "hello my name"
son3.age = 100
print("name:\(son3.name), age:\(son3.age)")
// override new name = hello my name
// override new age = 100
// name:override name, age:30
}
}

重写属性的限制

  1. 读写计算属性/存储属性,是否可以重写为只读计算属性?(权限变小,不可以)
    错误信息:// Cannot override mutable property with read-only property ‘xxx’
  2. 只读计算属性,是否可以在重写变成读写计算属性?(权限变大,可以)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Man3 {
var age:Int { // 计算属性
return 26
}
}

class Son4: Man3 {

override var age: Int{ // 计算属性
get{
return 30
}
set{
print("override new age = \(newValue)")
}
}
}

重写属性观察器

可以在属性重写中为一个继承来的属性添加属性观察器,那么当继承来的属性值发生改变时,就会检测到。

注意点:只能给非lazy属性存储属性设定属性观察器,不能给计算属性设置属性观察器,给计算属性设置观察器没有意义。这句话可能给人一个误解,给计算属性设置属性观察器不是不能,只是添加了没意义。

属性观察器限制:

  • 不能在子类中重写父类只读的存储属性
  • 不能给lazy的属性设置属性观擦器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
class Man4 {
var name: String = "daisuke"
var age:Int = 0{
willSet{
print("willSet:super new \(newValue)")
}
didSet{
print("didSet:super new \(oldValue)")
}
}
var height:Double{
get{
print("super get height")
return 10.0
}
}

var weight:Double{
get{
print("super get weight")
return 10.0
}
set{
print("super set weight")
}
}

lazy var array: [String] = {
return ["1", "2", "3"]
}()
}

class Son5: Man4 {
// 可以在子类中重写父类的存储属性为属性观察器
override var name: String{
willSet{
print("willSet:child name \(newValue)")
}
didSet{
print("didSet:child name \(oldValue)")
}
}
// 可以在子类中重写父类的属性观察器
override var age: Int{
willSet{
print("willSet:child age \(newValue)")
}
didSet{
print("didSet:child age \(oldValue)")
}
}

// 重写父类只读属性,报错
// Cannot observe read-only property 'height'; it can't change
// override var height: Double{
// willSet{
// print("willSet:child height \(newValue)")
// }
// didSet{
// print("didSet:child height \(oldValue)")
// }
// }

// 可以在子类重写父类的计算属性为属性观察器
override var weight: Double{
willSet{
print("willSet:child weight \(newValue)")
}
didSet{
print("didSet:child weight \(oldValue)")
}
}

// 这里重写lazy属性array,可能会误解,不是不可以重写父类lazy属性,是不可以给lazy属性`添加`观察器
override var array: [String]{
willSet{
print("willSet:child array \(newValue)")
}
didSet{
print("didSet:child array \(oldValue)")
}
}

}


class TestViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

let son5 = Son5()
son5.age = 100
print(son5.age)
/*
willSet:child age 100
willSet:super new 100
didSet:super new 0
didSet:child age 0
100
*/


son5.weight = 100.31
print(son5.weight)
/*
super get weight
willSet:child weight 100.31
super get weight
willSet:child weight 10.0
super get weight
10.0
*/


son5.array = ["a", "b", "c"]
print(son5.array)
/*
willSet:child array ["a", "b", "c"]
didSet:child array ["1", "2", "3"]
["a", "b", "c"]
*/

}
}

防止重写

利用final关键字防止重写,final关键字既可以修饰属性,也可以修饰方法,并且还可以修饰类。

  • 被final关键字修饰的属性和方法不能被重写
  • 被final关键字修饰的类不能被继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
final class Man5 {
final var name: String = "daisuke"
final var age:Int = 0{
willSet{
print("super new \(newValue)")
}
didSet{
print("super new \(oldValue)")
}
}

final func eat() {
print("吃饭")
}
}