构造方法
实例对象在被创建时,需要对存储属性初始化,两种方式:
方法一:在声明属性的同时设定初值(之前一直使用这种方式)
方法二:使用构造方法
构造方法init:在对象创建时自动调用并对实例对象的存储属性进行初始化
a. 无需func关键字声明
b. 参数列表里可以有多个参数,也可以没有参数,参数可以有默认值
c. 支持重写和重载
d. 没有返回值
如:
class Person{
var name:String
var age:Int
init(name:String, age:Int) {
self.name = name
self.age = age
}
init(name1:String) {
self.name = name1
self.age = 11
}
init(name2:String) {
self.name = name2
self.age = 12
}
init() {
self.name = "123"
self.age = 20
}
}
var p1:Person = Person()
print("name:\(p1.name) age:\(p1.age)")//name:123 age:20
var p2:Person = Person(name1: "iOS")
print("name:\(p2.name) age:\(p2.age)")//name:iOS age:11
var p3:Person = Person(name2: "iOS")
print("name:\(p3.name) age:\(p3.age)")//name:iOS age:12
var p4:Person = Person(name: "iPhone", age: 7)
print("name:\(p4.name) age:\(p4.age)")//name:iPhone age:7
init()方法是强制包含外部参数名的,即使未显式写出,也是有的,相当于参数名前有#,可以使用 _ 关闭这个强制功能
这里有四个重载的init()方法,其中有两个方法仅仅外部参数名不同,也可以进行重载(普通函数不可以)
构造方法中的常量:
常量属性,在构造方法内部是可以变的,即构造方法执行后才是真正的常量
注意:子类的构造方法不能修改父类常量属性的值
构造方法的分类
默认构造方法:适用于类、结构体、枚举
当所有的存储属性都有默认值时由系统自动生成
如果存储属性是一个可选类型,则不必设置默认值也能具有默认构造方法,其默认值为nil
指定构造方法:显式地写出并且可重载,需要为每个存储属性初始化
便捷构造方法:内部调用其他构造方法
需要在init()前加convenience关键字声明
只有在便捷构造方法中可以显式调用构造方法,如:
convenience init() {
self.init(name:"123", age:20)
}
可失败构造方法:在某些情况下构造对象失败后,返回nil
返回nil,意味着返回值类型是一个可选类型,语法:init?()
可失败构造方法可以声明为便捷构造方法
如:
convenience init?(age:Int) {
self.init(name:"123", age:age)
if ( age < 0 ) {
return nil
}
}
var p1:Person? = Person(age: 5)
隐式解绑的可失败构造方法,即返回值是解绑后的,语法init!()
也就意味着返回值不再是一个可选类型,也就意味着可失败构造方法返回nil程序会崩溃
继承中的构造过程
先来看一段代码:
class Animal {
var age:Int
init() {
self.age = 10
print("Animal init.")
}
}
class Dog:Animal {
var name:String
override init() {
self.name = "abc"
print("Dog init.")
}
}
var p = Dog()
print("age:\(p.age) name:\(p.name)")
输出的结果:
Dog init.
Animal init.
age:10 name:abc
重要说明:子类的构造方法一定调用了父类的构造方法完成父类部分的初始化
父子类构造方法的调用顺序:
上面例子中,在子类的init()中并没有显式调用,则父类的init()实在子类init()的最后隐式调用
存在的问题是:在子类的init()中不能访问父类的属性
解决方法:子类的init()中显式调用父类构造方法
override init() {
self.name = "abc"
super.init()
self.age++
print("Dog init.")
}
需要注意的是:显式调用父类构造方法,必须在子类存储属性初始化后进行
子类也可以添加构造方法,子类构造方法内部也可以指定调用其他构造方法
class Animal {
var age:Int
init() {
self.age = 10
}
init(age:Int) {
self.age = age
}
}
class Dog:Animal {
var name:String
override init() {
self.name = "abc"
super.init()
self.age++
}
init(age:Int, name:String) {
self.name = name
super.init(age: age)
}
}
var p = Dog(age: 22, name: "ABC")
print("age:\(p.age) name:\(p.name)")
析构方法
与构造方法相反,对象即将被销毁前,会自动调用析构方法
a. 析构方法 deinit 不带任何参数也没有返回值,不用func修饰
b. 有默认析构方法,也可以显式写出代替
c. 析构方法不能重载
d. 子类实例对象销毁时,先调用子类的析构方法,再调用父类的析构方法
e. 任何位置都不能显式调用deinit
class Animal {
var age:Int
init() {
self.age = 10
}
init(age:Int) {
self.age = age
}
deinit {
print("Animal deinit.")
}
}
class Dog:Animal {
var name:String
override init() {
self.name = "abc"
super.init()
self.age++
}
init(age:Int, name:String) {
self.name = name
super.init(age: age)
}
deinit {
print("Dog deinit")
}
}
var p = Dog(age: 22, name: "ABC")
print("age:\(p.age) name:\(p.name)")
p = Dog() //基于ARC机制,此时上一个Dog实例对象被销毁
输出结果:
age:22 name:ABC
Dog deinit
Animal deinit.