Swift -- 构造过程
构造过程是使用类、结构体或枚举类型的实例之前的准备过程。在新实例可用前必须执行这个过程,具体操作包括设置实例中每个存储属性的初始值和执行其他必须的设置或初始化工作。
存储属性的初始赋值
类和结构体在创建实例时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。
注意:当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观察者。
- 构造器
在创建某个特定类型的新实例时被调用。它的最简形式类似于一个不带任何参数的实例方法,已关键字init
命名。
init(){
//在此处执行构造过程
}
- 默认属性值
可以在构造器中为存储属性设置初始值,同样,可以在属性声明时为其设置默认值。
自定义构造过程
- 构造参数
可以在定义中提供构造参数,指定所需值得类型和名字。构造参数的功能和语法跟函数和方法的参数相同
struct A {
var num : Double;
init(value : Double) {
num = value;
}
init(newValue : Double) {
num = newValue * 2;
}
}
let a = A(value: 10);//num值为10;
let b = A(newValue: 10);//num值为20
- 不带外部名的构造器参数
如果你不希望为构造器的某个参数提供外部名字,可以使用_
来显示描述它的外部名。
struct A {
var num : Double;
init(_ value : Double) {
//隐藏外部名
num = value;
}
init(out newValue : Double) {
//out为外部名
num = newValue * 2;
}
}
let a = A(10);//num值为10;
let b = A(out: 10);//num值为20
- 可选属性类型
如果你定制的类型包含一个逻辑上允许取值为空的存储舒心,无论是因为它无法再初始化赋值,还是因为它在之后某个时间点可以赋值为空,你都需要将他定义为可选类型
。可选类型的属性将自动初始化为nil
,表示这个属性是有意在初始化时设置为空的。
class myclass{
var text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
let my = myclass(text: "geyang");
my.ask();
我们将属性response
声明为String?
类型,或者说是可选字符串类型
,当myclass
实例化时,它将自动赋值为nil
,表明此字符串暂时还没有值。
默认构造器
如果结构体或类的所有属性都有默认值,同事没有自定义的构造器,那么Swift会给这些结构体或类提供一个默认构造器。这个默认构造器将简单地创建一个所有属性都设置默认值得实例。
class myclass{
var text: String = "how old are you";
var response: String?
}
let my = myclass();//默认构造器
由于该类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个为所有属性设置默认值得默认构造器。
类的继承和构造过程
-
指定构造器和便利构造器
指定构造器
是类中最主要的构造器,一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。
每一个类都必须拥有至少一个指定构造器。
便利构造器
是类中比较次要的、辅助型的构造器,你可以定义便利构造器
来调用同一个类中的指定构造器
,并未其参数提供默认值,你也可以定义比那里构造器来创建一个特殊用途或特定输入值的实例。
你应当只在必要的时候为类提供便利构造器
,比方说某种情况下通过使用便利构造器
来快捷调用某个指定构造器
,能够节省更多开发时间并让类的构造过程更清晰明了。 -
指定构造器和遍历构造器的语法
类的指定构造器的写法跟值类型简单构造器一样:
init(paramters){
statements;
}
便利构造器也采用相同样式的写法,但需要在init
关键字之前防止convenience
关键字,并使用空格将它们俩分开:
convenience init (paramter){
statements;
}
- 类的构造器代理规则
为了简化指定构造器和便利构造器之间的调用关系,Swift采用以下三条规则来限制构造器之间的代理调用:
1.指定构造器必须调用其直接父类的指定构造器
2.便利构造器必须调用同类中定义的其它构造器
3.便利构造器必须最终导致一个指定构造器被调用
一个更方便记忆的方式: - 指定构造器必须总是向上代理
- 便利构造器必须总是横向代理
//便利构造器
class A{
var a : Int;
var b : Int;
//便利构造器
convenience init(b : Int){
self.init(a: 0, b: b);
}
//指定构造器
init(a : Int, b : Int) {
self.a = a;
self.b = b;
print("class ab init!");
}
}
可失败的构造器
如果一个类,结构体或枚举类型的对象,在构造过程中有可能失败,则为定义一个可失败的构造器,这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。
为了妥善处理这种构造过程中可能会失败的情况,你可以在一个类,结构体或枚举类型定义中,添加一个或多个可失败构造器,其语法为init
关键字后面添加问号(init
?)。
可失败构造器会创建一个类型为自身类型的可选类型的对象,你通过return nil
语句来表明可失败构造器在何种情况下“失败”。
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
定义了一个名为 Animal
的结构体,其中有一个名为 species
的 String
类型的常量属性。同时该结构体 还定义了一个接受一个名为 species
的 String 类型参数的可失败构造器
。这个可失败构造器检查传入的参数是否 为一个空字符串。如果为空字符串,则构造失败。否则,species
属性被赋值,构造成功。
必要构造器
在类的构造器前添加required
修饰符表明所有该类别的子类都必须实现构造器
class SomeClass{
required init(){
}
}
在子类重写父类的必要构造器时,必须在子类的构造器前也添加 required
修饰符,表明该构造器要求也应用于继承链后面的子类。在重写父类中必要的指定构造器时,不需要添加 override
修饰符。
通过闭包或函数设置属性的默认值
如果某个存储型属性的默认值需要一些定制或设置,你可以使用闭包或全局函数为其提供定制的默认值。每当某
个属性所在类型的新实例被创建时,对应的闭包或函数会被调用,而它们的返回值会当做默认值赋值给这个属
性。
这种类型的闭包或函数通常会创建一个跟属性类型相同的临时变量,然后修改它的值以满足预期的初始状态,最
后返回这个临时变量,作为属性的默认值。
class SomeClass {
let someProperty: SomeType = {
// 在这个闭包中给 someProperty 创建 一个默认值
// someValue 必须和 SomeType 类型相同
return someValue
}()
}
注意闭包结尾的大括号后面接了一对空的小括号
。这用来告诉 Swift 立即执行此闭包
。如果你忽略了这对括 号,相当于将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。