每天学一点Swift----面向对象下(三)

二.类的构造和析构(续)

1. Swift中,子类不会自动继承父类的构造器,只有满足如下规则时,子类才会自动继承父类的构造器:

(1)规则1:如果子类没有提供任何指定构造器,那么它将自动继承父类的所有指定构造器

(2)规则2:如果子类实现了父类所有的指定构造器,无论是通过规则1继承实现的,还是通过程序编码实现的,它都将自动继承父类的所有便利构造器。

2.如果子类中定义的构造器与父类中指定构造器的形参列表、外部形参名相同,即可认为子类构造器重写了父类构造器,此时需要在构造器前面用override关键字修饰----override关键字强制Swift编译器执行严格的检查,以保证该构造器确实重写了父类构造器。

3.只要子类构造器重写了父类的指定构造器,则必须在子类构造器前添加override修饰符,即使子类构造器是一个便利构造器(此时便利构造器前同时出现override convenience两个修饰符)

4.对于父类的便利构造器来说:如果子类中定义的构造器只是与父类中便利构造器的形参列表、外部形参名相同----虽然看起来很像重写父类构造器,但由于子类永远不可以直接调用父类的便利构造器,因此这其实不算构造器的重写,所以也不用override关键字。

5.类与可能失败的构造器:对于值类型(枚举、结构体)的可能失败的结构体而言,在该构造器实现体的任意地方都可以通过return nil来触发构造失败。对于类来说,可能失败的构造器必须满足如下两个条件之后才能触发:

(1)该类中的所有实例存储属性都已经悲哀赋初始值(既可由程序显式地指定初始值,也可由系统隐式分配默认的初始值)

(2)所有的构造器调用都已经被执行(即:return nil不能位于self.init(参数)或super.init(参数)代码之前)

6.举个栗子:

class User

{

var name : String

init?(name : String)

{

//必须先对name实例存储属性设置初始值,然后才能触发构造失败

self.name = ""

如果传入的name参数为空字符串,构造失败,返回nil

if name.isEmpty

{

return nil

}

self.name = name

}

}

上面代码中,如果将self.name = ""去掉,将会导致编译错误。如果希望在去掉的情况下不报错,也可以将name声明为可选类型:var name : String!

7.可能失败的构造器的传播:类、结构体、枚举的构造器可以横向调用该类型中另一个可能失败的构造器,类似地,子类的可能失败的构造器也可以向上调用父类的可能失败的构造器。

8.可能失败的构造器可以调用同一个类中的普通构造器,反过来,普通构造器不能调用同一个类中可能失败的构造器。但是在结构体中,普通构造器却可以调用同一个结构体中可能失败的构造器。

9.由于可能失败的构造器会产生传播,因此当可能失败的构造器在构造过程失败之后,这种失败行为会立即阻止原来构造器代码继续向下执行。

10.举个栗子:

class Student : User

{

var grade : Int!

init!(name : String, grade : Int)

{

//调用父类的可能失败的构造器

super.init(name : name)

//如果grade小于1,则使用return nil触发构造失败

if grade < 1

{

return nil

}

self.grade = grade

}

}

let s1 = Student(name : "", grade : 3)

print(s1 == nil)

因为name为空,所以在super.init处返回了nil,因此super.init之后的所有代码都不会被执行。

11.重写可能失败的构造器:子类可以重写父类的可能失败的构造器,子类既可以用可能失败的构造器,也可以用普通的构造器重写父类的可能失败的构造器----这样可以保证被重写后的构造器不可能失败。

12.注意:子类的普通构造器可以重写父类的可能失败的构造器,但子类的普通构造器不能向上调用父类的可能失败的构造器。对于类的构造器而言,普通构造器永远不能调用可能失败的构造器,不管是父类的可能失败的构造器还是同一个类中可能失败的构造器。

13. Swift中运行在父类构造器前面添加required关键字,该关键字用于声明所有子类必须包含该required构造器。

14.父类中声明的required构造器既可以是指定构造器,也可以是便利构造器。

15. required关键字***只***要求所有子类必须包含required构造器,至于子类是编码实现,还是继承得到的,Swift并不关心,至于子类采用指定构造器还是便利构造器,Swift也不关心。

16.举个栗子:

class Fruit

{

var name : String

var weight : Double

//定义required的指定构造器

required inti(name : String)

{

self.name = name

self.weight = 0.0

}

//定义required的便利构造器

required convenience init(name : String, weight : Double)

{

self.init(name)

self.weight = weight

}

}

class Apple : Fruit

{

var color : String

//重写父类的required的便利构造器

required init(name : String, weight : Doublee)

{

self.color = "pink"

super.init(name : name)

}

init(color : String)

{

self.color = color

super.init(name : "")

}

//重写父类的required的指定构造器

//虽然此处属于构造器重写,但无须添加override

required convenience init(name : String)

{

self.init(color : "aaa"

super.name = name

}

}

//该类将会继承得到父类的required构造器

class Grape : Fruit

{

var sugarRate : Double = 0.45

}

因为Grape中没有定义任何指定构造器,因此,它将会得到父类的所有指定构造器和便利构造器。

17.析构器:在实例被销毁之前,程序可能需要释放一些物理资源(并非释放内存,内存释放由系统自动完成),比如关闭文件、断开网络链接等等,此时可以借助于析构器来实现。

18.析构器是一个名为deinit的函数(也无须用func关键字)析构器deinit没有返回值,也没有参数(甚至不能有圆括号)因此不能重载它。

19.析构器在实例释放之前由系统自动调用,因此不要主动调用析构器。子类自动继承父类的析构器,如果子类实现了自己的析构器,子类析构器执行结束时将自动调用父类的析构器。无论如何,子类析构器一定会调用父类析构器。

20.由于只有等到析构器被调用完成后,该实例才会被被销毁,因此析构器可以访问该实例的所有实例存储属性,或者根据这些属性来关闭资源。

21.举个栗子:

class Fruit

{

var name : String

var weight : Double

init(name : String)

{

self.name = name

self.weight = 0.0

}

deinit

{

print("in Fruit -- this object will be destoryed")

}

}

class Apple : Fruit

{

var color : String

init(name : String, weight : Double, color : String)

{

self.color = color

super.init(name : name)

}

deinit

{

print("in Apple -- this object will be destoryed")

}

}

注意:应该使用swiftc命令编译该程序,然后再运行,才可以看到预想的结果,再Palyground中运行该程序将看不到该效果。因为:Playground只是一个临时性的练习场所。

你可能感兴趣的:(每天学一点Swift----面向对象下(三))