Swift -- 8.2 类的构造与析构
类中的存储属性必须要在构造过程中设置初始值,加上继承的的属性,类的构造过程比较复杂
析构器 deinit函数
在实例销毁之前被系统调用,实例不能自己调用
值类型的构造器可以直接调用一个其他的构造器,但是类的构造器不可以,必须要用convenience来变成便利构造器,然后才能调用其他的构造器
如果有父类,需要使用super来调用父类的构造器
class Apple1 { var name:String! var weight:Double init(name:String, weight:Double){ self.name = name self.weight = weight } init(nn name:String, weight:Double){//构造器重载 self.name = name self.weight = weight } convenience init(n name:String, w weight:Double){//变成便利构造器来调用其他构造器 self.init(name:name, weight:weight) self.name = name } } class Pear: Fruit { init(weight:Double){ super.init()//调用父类的构造器 self.weight = weight } }
两段式构造
swift类的构造需要两个阶段完成
第一阶段:先为存储属性分配内容,使用本类的构造器初始化由本类定义的存储属性,然后沿着构造器链向上,逐个调用各父类构造器初始化对应父类定义的存储属性
第二阶段:从最顶层开始,沿着顶部构造器链向下,每个构造器都可以再次修改存储属性
为了保证两段式构造顺利完成,swift提供了四种安全检查
1:指定构造器必须先初始化当前类中定义的实例存储属性,然后才能向上调用父类构造器
2:指定构造器必须先向上调用父类构造器,然后才能对继承得到的属性赋值
3:便利构造器必须先调用同一个类的其他构造器,然后才能对属性赋值
4:构造器在第一阶段完成之前,不能调用实例方法,不能读取实例属性
swift类的构造器规则比较繁琐,因此处理起来比较容易出错,但如果程序显式地为所有实例存储属性指定初始值,定义构造器时就会简单得多,而且可以直接利用系统默认的构造器(hiahiahia 因为这样做的话,只需要把所有的构造器都写在第一行就永远不会出错了hiahiahia)
class A{ var name : String init(name:String){ self.name = name } } class B: A { var age:Int init(age:Int){ self.age = age super.init(name: "") self.run() } func run(){ print("run"); } } var bb = B(age: 22)
构造器的继承和重写
构造器的继承规则
1 如果子类没有提供任何构造器,那么它会继承父类的所有指定构造器和便利构造器
2 如果子类实现了父类的所有指定构造器(比如说使用方法1或是重写来实现),那么它将继承父类的所有便利构造器
反而言之,如果子类自己写了构造器,并且没有重写父类的指定构造器,那么它就不能继承父类的构造器
class F { var name : String var weight : Double init(name:String, weight:Double){ self.name = name self.weight = weight } init(n name:String){ self.name = name self.weight = 0.0 } convenience init(){ self.init(n:"") self.weight = 1.0 } } class App: F { var color : String override init(name:String, weight:Double){ self.color = "" super.init(name: name, weight: weight) } override init(n name:String){ self.color = "" super.init(n: name) } init(name:String, weight:Double, color:String){ self.color = color super.init(name: name, weight: weight) } convenience init(name:String, color:String){ self.init(name:name, weight:0.0, color:color) } } class Fuji: App { var vitanim : Double = 0.21 } var ff = App() ff.weight var tt = Fuji(name: "", color: "") //子类的构造器不能与父类的构造器的形参列表相同,如果相同需要添加override关键字。但是如果父类的构造器是便利构造器就没有事 class F2 { var name : String init(){ self.name = "" } convenience init(name:String){ self.init() } } class F3: F2 { var color : String init(name:String){//这个构造器父类中也有,不过父类中是便利构造器,所以子类中依然可以使用 self.color = "" super.init() } }
类与可能失败的构造器
可能失败的构造器必须满足的两个条件
1 该类中所有实例存储属性都已被赋值
2 所有的构造器调用都已经被执行(return nil 不能位于self.init 或 super.init代码之前)
如果子类调用父类的可能失败的构造器,那么如果父类的构造器return nil,那么子类构造器中之后的代码都不会被执行
在重写可能失败的构造器的时候,可以重写成可能失败的构造器,也可以重写成普通构造器
class User { var name : String init?(name:String){ self.name = "" if name.isEmpty { return nil } self.name = name } } class Student: User { var grade : Int! init!(name:String, grade:Int){ super.init(name: name)//如果这个构造器失败,那么之后的代码都不会被执行 if grade < 1{ return nil } self.grade = grade } } //所以在类中,可能失败的构造器可以调用普通的构造器,普通的构造器不能调用可能失败的构造器 class C1 { var name : String init!(name:String){ self.name = "" if name.isEmpty{ return nil } self.name = name } } class C2: C1 { var color : String init?(name:String, color:String){ self.color = "" super.init(name: "") } init(color:String){ self.color = color super.init(name: "")//虽然这里没有报错,但是使用实例的时候会出错的 } } let ccccc = C2(name: "", color: "")
子类必须包含的构造器
使用required来标记构造器,表示子类构造器必须包含这个构造器
可以通过重写和继承来得到
class Fruit5 { var name:String required init(name:String){ self.name = name } } class Apple5: Fruit5 { var weight : Double init(weight:Double){ self.weight = weight super.init(name: "") } required init(name: String) {//不写这个构造器会报错 self.weight = 0 super.init(name: name) } }
析构器
deinit
在实例被销毁之前调用
析构器是系统自动调用的,实例不可以自己调用
子类的析构器执行结束时将自动调用父类的析构器
class T1 { var name : String = "nn" deinit{ print("the T1 is deinit") } } class T2: T1 { deinit{ print("the T2 is deinit") } } var ap:T2? = T2() ap!.name ap = nil //the T2 is deinit //the T1 is deinit