1、Swift入门学习笔记(第一版),对Swift的基础知识点进行梳理总结。知识点一直在变,只是作为参考,以苹果官方文档为准~
2、在学习完基本的知识点以后会结合官方文档及相关资料,在此版本的基础上进行添加更改。
十四、构造过程(1)
构造过程:新实例可用前必须执行这个过程,包括设置实例中每个存储属性的初始值,和其他必须得设置和初始化工作
Swift的构造器区别于OC,不需返回值,只要保证正常初始化即可(可失败构造器return nil下文有提到)
1、存储属性的初始赋值/默认属性值
可以直接设置默认值或者在构造器中赋值,它们的值是被直接设置的,不会触发任何属性观察器
如果一个属性总使用相同的初始值,默认值比构造器赋值要好。默认值好在其一清晰且可自动推导,其二可充分利用默认构造器、构造器继承等特性
struct Student {
var averageAge:Int = 22 //默认属性值
var age:Int
init() {
age = 21 //构造器赋值
}
}
var s = Student()
print("Student age is \(s.age)")
Output:
Student age is 21
2、自定义构造过程
可输入参数和可选属性类型来自定义构造过程,也可在构造过程中修改常量属性
2.1、构造参数
struct Color {
let red,green,blue:Double
//可选属性类型:逻辑上允许取值为空的存储型属性,自动初始化为nil。
//在此处的意思是你要告诉我三原色值我才能知道这个颜色名字,刚刚开始是nil(只是一个例子,不要当真。T^T)
var colorName:String?
//构造参数外部参数名与内部参数重名时,self区分
init(red:Double,green:Double,blue:Double) {
self.red = red
self.green = green
self.blue = blue
}
init(white:Double) {
red = white
green = white
blue = white
}
//不带外部参数名,'_'省略
init(_ white:Double) {
red = white+0.2
green = white+0.2
blue = white+0.2
}
func infoPrint() {
print("red:\(self.red),green:\(self.green),blue:\(self.blue)")
}
}
let testColor0 = Color(red: 1.0,green: 1.0,blue: 1.0)
let testColor1 = Color(white: 0.6)
let testColor2 = Color(0.6)
testColor0.infoPrint()
testColor1.infoPrint()
testColor2.infoPrint()
Output:
@H_301_159@red:1.0,@H_301_159@green:1.0,@H_301_159@blue:1.0
@H_301_159@red:0.6,@H_301_159@green:0.6,@H_301_159@blue:0.6
@H_301_159@red:0.8,@H_301_159@green:0.8,@H_301_159@blue:0.8
注意:构造器区别于方法和函数,都是init
没有辨别性。在调用时,主要通过构造器的参数名和类型来确定调用的构造器。当定义的构造器没有外部参数名时,Swift会为构造器的参数生成与内部名相同的外部名,相当于每个构造参数值钱加了一个哈希符号
在构造过程中任意时间都可修改常量属性的值,只要在构造结束时是确定值即可,一旦被赋值将永远无法更改
对于类的实例,常量属性只能在定义它的类的构造过程中修改,不能在子类中修改
3、默认构造器
如果结构体和类所有属性都有默认值,且没自定义构造器,Swift会创建一个默认构造器,将所有属性设置为默认值
class Dog {
var name:String?
var age:Int = 2
}
var dog = Dog()
print("dog:\(dog.name),\(dog.age)")
Output:
dog:nil,2
3.1、结构体的逐一成员构造器
注意类没有!
结构体的所有存储属性提供默认值且没有提供构造器,他们会自动获得逐一成员构造器
struct Rectangle {
var width = 0.0,height = 0.0
}
let rectangle = Rectangle(width: 2.0,height: 18.0) //逐一成员构造器
print("rectangle:\(rectangle.width),\(rectangle.height)")
Output:
@H_301_159@rectangle:2.0,18.0
4、值类型的构造器代理
值类型:结构体和枚举
构造器可以调用其他构造器来完成实例的部分构造过程,减少多个构造器间的代码重复
可以在自定义的构造器内部通过self.init
调用其他构造器,实现构造器代理
注意:如果已有定制构造器,那么将无法访问默认构造器或者结构体的逐一成员构造器,这个限制你定义更复杂的构造器,而误以为是自动生成的构造器
但是如果想通过默认或逐一实现创建实例,可以将自己定制的构造器写到扩展中,而不是跟值类型定义混在一起,这部分在扩展中解读
5、类的继承和构造过程
类的存储型属性,即使是继承自父类的属性,都必须在构造过程中设置初始值
5.1、指定构造器和便利构造器
指定构造器可初始化类中所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化
便利构造器是辅助性构造器,用来调用同一类中的指定构造器,为参数提供默认值。也可用来创建特殊用途或特定输入的实例
每个类都必须拥有指定构造器,而只有必要的时候才提供便利构造器
指定构造器:
init(parameters) { statements }
便利构造器:
convenience init(parameters) { statements }
5.2、类的构造器代理原则
三条规则:
a、指定构造器必须调用其直接父类的指定构造器
b、便利构造器必须调用同一类中定义的其他构造器
c、便利构造器必须最终以调用一个指定构造器结束
简言之:
指定构造器必须总是向上代理
便利构造器必须总是横向代理
上述规则由下面图例来说明
5.3、两段式构造过程
第一阶段:每个存储型属性引入类构造器设置初始值
第二阶段:给每个类一次机会在新实例使用之前进一步定制他们的存储型属性
两段式让构造过程更安全,整个类层级结构中每个类灵活性更高。防止属性值在初始化之前被访问,同时防止属性被另一个构造器以外地赋予不同的值
基于安全检查的两段式构造过程演示:
阶段一:
a、一个指定构造器或便利构造器被调用
b、分配新实例所需内存,但内存没有初始化
c、指定构造器确认该类引入的所有存储型属性已赋初值,内存完成初始化(因为之后要向上代理)
d、指定构造器调用父类构造器,完成父类属性初始化(即父类的c操作)
e、循环d操作指导构造器链最顶部
到达顶部,且确保实例所包含的存储型属性都已赋值,这个实例的内存被认为已经完成初始化
阶段二:
a、从顶部构造器往下,每个指定构造器进一步定制实例,可访问self、修改属性并调用实例方法
b、构造链中的便利构造器可有机会定制实例和使用self
编译器的4种安全检查:
a、向上代理之前,所有新引入的属性赋初值完成初始化
b、向上代理父类后,才能为继承属性设置新值—>否则赋予的新值会被父类的构造器覆盖
c、便利代理其他构造器,再赋值或调实例方法—>否则新值会被其他构造器覆盖
d、完成后才能调实例,访问属性值,不能把self当值访问,但在这之前是可以self.property给属性赋值
新引入赋初值->向上代理->访问继承属性
便利代理其他->访问属性或调实例方法
类方法是可以调的!!
//代码演示:
class Animal {
var life: Int
var height: Double
init(getLife: Int,getHeight: Double) {
self.life = getLife
self.height = getHeight
}
}
5.4、构造器的继承和重写
区别于OC,Swift的子类不会默认继承父类的构造器
(但是在确定和安全的情况下是可被继承的,参见下文自动构造器的继承)
子类要用构造器,必须自己写,如果构造器跟父类的一样,那么要加override修饰符,编译器会去检查父类是否有相匹配的重写指定构造器和验证重写构造器的参数。不一样,就不用加,Swift子类不会继承父类构造器
class Animal {
var life: Int
var height: Double
init(getLife: Int,getHeight: Double) {
self.life = getLife
self.height = getHeight
}
}
class Pig:Animal {
var name:String
override init(getLife: Int,getHeight: Double) {
self.name = "Hu"
super.init(getLife: getLife,getHeight: getHeight)
}
}
5.5、自动继承父类构造器
前提条件:子类新引入的属性都有默认值
再满足以下规则那么会自动继承
规则1:子类没有定义任何指定构造器,那么子类将自动继承父类指定构造器
规则2:如果子类实现父类所有指定构造器(可以是规则1继承,也可以是重写的),那么子类将自动继承父类所有便利构造器
如果子类添加了更多的便利构造器,这两个规则仍然适用
class Cow:Animal {
var name:String = "Little Cow" //新引入属性都有默认值,继承所有指定构造器
}
let cow = Cow(getLife: 5,getHeight: 1.0) //可以调用父类的指定构造器