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

  

 

posted @ 2016-06-28 15:22  电蚊拍  阅读(187)  评论(0编辑  收藏  举报