结构体和类的实例在构造过程中会调用一种特殊的init方法,称为构造函数。构造函数没有返回值,可以重载。在多个构造函数重载的情况下,运行环境可以根据它的外部参数名或参数列表调用合适的构造函数。
默认构造函数
结构体和类在构造过程中会调用一个构造函数,即便是没有编写任何构造函数,编译器也会提供一个默认的构造函数。下面看示例代码:
- class Rectangle {
- var width: Double = 0.0
- var height: Double = 0.0
- }
- var rect = Rectangle() //创建实例,并调用默认构造函数init()
- rect.width = 320.0
- rect.height = 480.0
- print("长方形:\(rect.width) x \(rect.height)")
- Rectangle()表示调用了某个方法,这个方法就是默认构造函数init()。
- 事实上,在Rectangle的定义过程中省略了构造函数,相当于如下代码:
- class Rectangle {
- var width: Double = 0.0
- var height: Double = 0.0
- init() {
- }
- }
如果Rectangle是结构体,则它的定义如下:
struct Rectangle {
var width: Double = 0.0
var height: Double = 0.0
}
而结构体Rectangle的默认构造函数与类Rectangle的默认构造函数是不同的,相当于如下代码:
- struct Rectangle {
- var width: Double = 0.0
- var height: Double = 0.0
- init() {
- }
- init(width: Double, height: Double) { //有参数的构造函数
- self.width = width
- self.height = height
- }
- }
要调用哪个构造函数是根据传递的参数名和参数类型决定的。
在构造函数中可以使用构造函数代理帮助完成部分构造工作。类构造函数代理分为横向代理和向上代理,横向代理只能在发生在同一类内部,这种构造函数称为便利构造函数。向上代理发生在继承的情况下,在子类构造过程中,要先调用父类构造函数初始化父类的存储属性,这种构造函数称为指定构造函数。
构造函数调用规则
- Person和Student类示例:
- class Person {
- var name: String
- var age: Int
- func description() -> String {
- return "\(name) 年龄是: \(age)"
- }
- convenience init () { //便利构造函数
- self.init(name: "Tony")
- self.age = 18
- }
- convenience init (name: String) { //便利构造函数
- self.init(name: name, age: 18)
- }
- init (name: String, age: Int) { //指定构造函数
- self.name = name
- self.age = age
- }
- }
- class Student: Person {
- var school: String
- init (name: String, age: Int, school: String) { //指定构造函数
- self.school = school
- super.init(name: name, age: age)
- }
- convenience override init (name: String, age: Int) {//便利构造函数
- self.init(name: name, age: age, school: "清华大学")
- }
- }
- let student = Student()
- print("学生: \(student.description())")
构造函数之间的调用形成了构造函数链,如图所示。
Swift限制构造函数之间的代理调用的规则有3条,如下所示。
指定构造函数必须调用其直接父类的的指定构造函数。从图可见,Student中的④号指定构造函数调用Person中的③号指定构造函数。
- 便利构造函数必须调用同一类中定义的其他构造函数。从图可见,Student中的⑤号便利构造函数调用同一类中的④号便利构造函数,Person中的①号便利构造函数调用同一类中的②号便利构造函数。
- 便利构造函数必须最终以调用一个指定构造函数结束。从图可见,Student中的⑤号便利构造函数调用同一类中的④号指定构造函数,Person中的②号便利构造函数调用同一类中的③号指定构造函数。
- ==================================================
- Swift中的子类构造函数的来源有两种:自己编写和从父类继承。并不是父类的所有的构造函数都能继承下来,能够从父类继承下来的构造函数是有条件的,如下所示。
- 条件1:如果子类没有定义任何指定构造函数,它将自动继承所有父类的指定构造函数。
- 条件2:如果子类提供了所有父类指定构造函数的实现,无论是通过条件1继承过来的,还是通过自己编写实现的,它都将自动继承所有父类的便利构造函数。
下面看示例代码:
- class Person {
- var name: String
- var age: Int
- func description() -> String {
- return "\(name) 年龄是: \(age)"
- }
- convenience init () {
- self.init(name: "Tony")
- self.age = 18
- }
- convenience init (name: String) {
- self.init(name: name, age: 18)
- }
- init (name: String, age: Int) {
- self.name = name
- self.age = age
- }
- }
- class Student: Person {
- var school: String
- init (name: String, age: Int, school: String) {
- self.school = school
- super.init(name: name, age: age)
- }
- convenience override init (name: String, age: Int) {
- self.init(name: name, age: age, school: "清华大学")
- }
- }
- class Graduate: Student {
- var special: String = ""
- }
来看看符合条件1的继承,Graduate继承Student,Graduate类没有定义任何指定构造函数,它将自动继承所有Student的指定构造函数。符合条件1后,Graduate从Student继承了如下指定构造函数:
init (name: String, age: Int,school: String)
再看符合条件2的继承,由于Graduate实现了Student的所有指定构造函数,Graduate将自动继承所有Student的便利构造函数。符合条件2后,Graduate从Student继承了如下3个便利构造函数:
init (name: String, age: Int)
init (name: String)
init ()
Student继承Person后有4个构造函数。
条件1对Student不满足,因为它有指定构造函数,Student类中的便利构造函数init (name: String, age: Int)满足了条件2,它实现了父类指定构造函数init (name: String, age: Int)。另外,由于子类构造函数与父类构造函数参数相同,需要使用override关键字,表示子类构造函数重写(overriding)了父类构造函数。
由于Student类实现了父类指定构造函数,因此也继承了父类的另外两个便利构造函数。