这是我参与更文挑战的第15天,活动详情查看: 更文挑战
初始化
初始化可以看做是用来创建新实例的特殊方法,与 OC 的初始化方法不同,OC 是先调用父类的init
再写子类的init
, 但在 Swift 里面, 先初始化子类init
, 再初始化父类init
。swift 中初始化无需返回值,主要任务是保证新实例在第一次使用前完成正确的初始化
关于存储属性设置初始化值
-
类和结构体创建实例时,必须为所有存储类型设置初始值
-
存储属性可以在初始化中设置, 也可以在声明存储属性的时候设置
-
在初始化中为存储属性设置默认值或赋值,不会触发任何属性观察者
注意:
在 Swift 中,如果存储属性没有在声明时赋值或没有在初始化时赋值,编译器会报错class Student { var name: String var age = 12 //默认属性值(如果一个属性总是使用相同的初始值,那么为其设置一个默认值比每次都在初始化中赋值要好。两种方法的效果是一样的,只不过使用默认值让属性的初始化和声明结合得更紧密。使用默认值能让初始化更简洁、更清晰,且能通过默认值自动推导出属性的类型) init() { name = "李雷" //将存储属性name的值初始化为"李雷" } } 复制代码
自定义初始化过程
初始化参数
自定义初始化支持函数参数标签,这种模式被称为外部形参。如果参数指定了外部形参标签,那么调用的时候就必须显示的使用,参数名称前面编写形参标签,用空格分隔
class Student {
var name: String
var age :Int
init(name nameStr:String, age ageValue:Int){
self.name = nameStr
self.age = ageValue
}
}
let stu = Student.init(name: "李雷", age: 12)
复制代码
参数标签和参数名称
在定义初始化时没有提供参数标签,可以直接使用参数名称来代替参数的参数标签
class Student {
var name: String
var age :Int
init(nameStr:String,ageValue:Int){
self.name = nameStr
self.age = ageValue
}
}
let stu = Student.init(nameStr: "李雷", ageValue: 12)
复制代码
不带外部名的初始化参数
初始化不需要参数的参数标签,那么用下划线_
代替参数的参数标签
class Student {
var name: String
init(_ nameStr:String){
self.name = nameStr
}
}
let stu = Student("李雷")
复制代码
可选属性类型
如果定义的类型包含一个逻辑上可能取值为空的存储型属性,无论是因为它无法在初始化时赋值,还是因为它在之后某个时间点可以赋值为空,都需要将它定义为可选类型。可选类型的属性会自动初始化为nil
,表示这个属性是有意在初始化时设置为空的
class Student {
var name:String
var age:Int? //自动赋值为nil
init(_ nameStr:String) {
name = nameStr
}
}
let stu = Student.init("李雷")
stu.age = 12
print("\(stu.name)今年\(stu.age!)岁")
log:
李雷今年12岁
复制代码
初始化过程中常量属性的修改
- 在初始化过程中的任意时间点都可以给常量属性赋值,只要在初始化过程结束时是一个确定的值,一旦常量属性被赋值,它将永远不可更改
- 常量属性只能在初始化期间由它本身的类修改,它不能被子类修改
class Student {
let name: String
init(_ name: String) {
self.name = name
}
}
let stu = Student.init("李雷")
复制代码
默认初始化
-
结构体和类有默认值
如果结构体或类的所有属性都有默认值,同时没有自定义的初始化,那么系统会给结构体或类设置一个默认的初始化,这个默认初始化创建的实例对象,其对象的所有属性值都为其默认值
class Student { var name: String? var age = 12 var writer = true } let stu = Student() 复制代码
由于Student类中的所有属性都有默认值,它将自动获得一个可以为所有属性设置默认值的默认初始化(尽管代码中没有显式为name属性设置默认值,但由于name是可选字符串类型,它将默认设置为nil)。上面例子中使用默认初始化创造了一个Student类的实例,并将其赋值给常量stu。
-
结构体没有默认值
如果结构体没有提供自定义初始化,它们将自动获得一个逐一成员初始化,即使结构体的存储属性没有默认值逐一成员初始化通过与成员属性名相同的参数名进行传值来完成对成员属性的初始化赋值
- 如果存储属性都没有默认值,默认有一种初始化
struct Student { var name: String var age: Int var writer: Bool } let stu = Student.init(name: "李雷", age: 12, writer: false) 复制代码
- 如果存储属性都部分有默认值,,默认有多种初始化
struct Student { var name: String var age = 12 var writer: Bool } let stu1 = Student.init(name: "李雷", age: 12, writer: false) let stu2 = Student.init(name: "韩梅梅", writer: true) 复制代码
值类型的初始化代理
- 初始化方法可以通过调用其它初始化方法来完成实例的部分构造过程,这个过程称为初始化代理
- 关于初始化代理对于值类型和引用类型是不同的,值类型因为不支持继承,所以只能把调用自己写的其他初始化方法,从而相对更简单。类则会从父类继承初始化方法的情况要考虑,不过还是那句话,所有存储属性在初始化中都完成设置初始值。
- 对于值类型,你可以使用
self.init
在自定义的初始化中引用相同类型中的其它初始化。并且你只能在初始化内部调用self.init
- 如果你为某个值类型定义了一个自定义的初始化,你将无法访问到默认初始化(如果是结构体,还将无法访问逐一成员初始化)
struct Size {
var width = 0.0 // 全部有默认值, 会生成2个初始化
var height = 0.0
}
struct Point {
var x = 0.0
var y = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
init(){} //在功能上和系统默认初始化是一样的
init(origin:Point,size:Size)//只是简单地将origin和size的参数值赋给对应的存储型属性
{
self.origin = origin
self.size = size
}
init(center:Point,size:Size)//先通过center和size的值计算出origin的坐标,然后再调用(或者说代理给)init(origin:size:)初始化来将新的origin和size值赋值到对应的属性中
{
let originX = center.x-(size.width / 2)
let originY = center.y-(size.height / 2)
// 初始化代理
self.init(origin: Point.init(x: originX, y: originY), size: size)
}
}
let rect1 = Rect.init()
let rect2 = Rect.init(center: Point.init(x: 1.0, y: 1.0), size: Size.init(width: 6.0, height: 6.0))
let rect3 = Rect.init(center: Point.init(x: 2.0, y: 2.0), size: Size.init(width: 6.0, height: 6.0))
复制代码