在swift中,类
class
、结构体struct
、枚举enum
都可以定义初始化器。实际开发中枚举自定义初始化用的比较少,枚举定义类型单一,下面主要讲解下类class
初始化用法。
枚举常规用法如下:
enum Season :Int {
case spring
case summer
case autumn
case winter
}
Season.spring
结构体常规用法如下:
struct Size {
init(x: Int,y:Int) {
print("x=\(x) ,y=\(y)")
}
}
var p = Size(x: 10,y: 20)
class
初始化器有两种:
- 1. 指定初始化器(designated Initializer)
- 2. 便捷初始化器(convenience initializer)
① 指定初始化器
/// 指定初始化器 init(parameters) { statements }
② 便捷初始化器
/// 便捷初始化器 convenience init(parameters) { statements }
注意:
- 每个类至少有一个指定初始化器,指定初始化器是类的主要初始化器,
- 每个类会默认生成
init() {}
格式的默认初始化器;- 可以自定义
init(x:Int,y:Int) {}
格式指定化初始化器。- 便捷初始化器 以
convenience
关键词开头,且内部必须调用指定初始化器self.init()
两种初始化器写法示例如下:
一、指定初始化器
class Point {
var x : Int = 0
var y : Int = 0
init(x:Int,y:Int) {
self.x = x
self.y = y
}
}
let p = Point(x: 10, y: 10)
二、便捷初始化器
class Point {
var x : Int = 0
var y : Int = 0
convenience init(x:Int,y:Int) {
//一定要调用,否则会报错
self.init()
self.x = x
self.y = y
}
}
let p = Point(x: 10, y: 10)
初始器相互调用规则:
- 规则1:
01.png
便捷初始化器最终必须调用同一个指定初始化器;
小结:
便捷初始化器一般是为了提供便捷的创建对象的方法,比如一个Point对象,他的指定初始化器(主要初始化器)是init(x:Int,y:Int) {}
方法,但是有时候我们只想设值x, 暂时不想赋值y, y只想后续再赋值,那么此时我们可以通过便捷初始化器提供一个convenience init(x:Int) {}
的创建方法。
此时,再次创建Point对象的时候,就会有该构建方法的提示,如下图所示:
02.png
可能有的人会反驳,说可以提供多个初始化方法,下图所示:
03.png
但是这种可能会有一个不好的方法,比如我们的
init(x:Int,y:Int) {}
指定初始化器(主要初始化器)里面可能不仅仅是赋值的操作,还会有一些其它的逻辑,所以我们创建这个Point 对象的时候需要执行init(x:Int,y:Int) {}
初始化方法。比如,不管什么初始化方法,我们都要执行
init(x:Int,y:Int) {}
指定初始化器(主要初始化器)里面的方法,便捷初始化器convenience init(x:Int) {}
就是一个很好的选择。便捷初始化器也就是为了方便开发者创建这个对象而提供的方法,直白的说也就是附带创建对象的方法,适合便捷初始化器。便捷初始化器都会调用
init(x:Int,y:Int) {}
指定初始化器的,所以这种构建方法比开发者自己提供多个初始化方法更加安全。
总结:
①. 一般把我们会把我们认为最主要的对象初始化方法定义为init() {}
指定初始化器(主要初始化器);
②. 其它附带的创建方法,间接调用指定初始化器
的我们把它设置为便捷初始化器;
- 规则2:
- 指定初始化器(主要初始化器)必须从相同的类里调用另一个初始化器;
如下面的代码就会提示报错:
04.png
正确的代码应该如下所示:
05.png
注意:
这里报错,主要是因为不可以直接访问父类的age
,因为这里有个规则:
指定初始化器必须调用它直系父类的指定初始化器;
因为指定初始化器是我们创建这个类必须要执行的步骤;
如果子类的指定初始化器不调用父类的指定初始化器的话,那么父类的初始化方法可能就无法执行,这样就会产生问题。
顺序:要求需要先把自己类的初始化属性赋值完,然后再调用父类初始化器。
疑惑总结:
(1). 指定初始化器不可以调用自己的指定初始化器,必须调用父类的指定初始化器。
06.png
07.png
(2).如果是自己的便捷初始化器,不可以调用父类的指定初始化器,必须调用自己的指定初始化器
08.png
09.png
(3).便捷初试化器可以调用自己的便捷初始化器
(4).指定初始化器(主要初始化器)必须从相同的类里调用另一个初始化器;
苹果初始化器相互调佣示意图:
010.png
Swift 初始化器的安全检查
- 指定初始化器必须保证再调用父类初始化器之前,其所在类定义的所有存储属性都要初始化完成;
- 指定初始化器必须先调用父类初始化器,然后才能为继承的属性设置新值;
- 便捷初始化器必须先调用同类中的其它初始化器,然后再为任意属性设置新值;
三、重写初始化器
开发中我们也会涉及到重写,但是一般都是加override 关键字重写方法,重写初始化器一般都是用在类继承里面,比如Student 继承Person 类,但是都有同一个 init(age:Int,score:Int)
初始化方法(参数个数,参数类型,参数标签名称完全一模一样),这种就是重写,如下所示:

总结:
- 当重写父类的指定初始化器时,必须加上override (即使子类的实现是便捷初始化器)
- 如果子类写了一个和父类一样的便捷初始化器,不用加override,因为这种情况严格意义上不算是重写;
013.png
因为上述这段代码所示意的情况,便捷初始化器只能被父类自己调用,不可能被子类调用,所以子类无法重写父类的便捷初始化器。
四、自动继承
- 如果子类没有自定义任何指定初始化器,它会自动继承父类所有的指定初始化器;
014.png
网友评论