Swift:关于 init 的位置

说明
首次发布 2018年12月12日
最近更新 2019年02月03日

类的构造代理规则

规则 1: 指定构造器必须调用其直接父类的的指定构造器;
规则 2:便利构造器必须调用同类中定义的其它构造器;
规则 3: 便利构造器必须最终导致一个指定构造器被调用。

一个更方便记忆的方法是:
• 指定构造器必须总是向上代理
• 便利构造器必须总是横向代理

以上,引用自《The Swift Programming Language (3.1)》。

类的初始化规则:一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。

两段式构造过程两个阶段

  • 第一阶段:每个类在使用之前必须调用自身的指定构造器初始化每个存储型属性。
  • 第二阶段:新实例准备使用之前进一步定制它们的存储型属性。

安全检查

  • 安全检查1:指定构造器必须先初始化当前类中定义的实例存储属性,然后才能向上调用父类构造器;
  • 安全检查2:指定构造器必须向上调用父类构造器,然后才能对继承得到的属性赋值;
  • 安全检查3:便利构造器必须先调用同一个类的其他构造器, 然后才能对属性赋值;
  • 安全检查4:构造器在第一阶段完成之前,不能调用实例方法,不能读取实例属性。

看下面两个代码示例,代码里给出了相应的注释:

示例一:

// 父类
class BaseHello {
    var age: Int
    var name: String?
    
    init(age: Int) {
        self.age = age
    }
}

// 子类
class ChildHello: BaseHello {
    var addr: String
    
    // `convenience initializer`,则调用自身的`designated initializer`,如self.init()
    convenience init(addr: String) {
        self.init(addr: addr, age: 18)
    }
    
    init(addr: String, age: Int) {
        // super.init(age: age) // 错误:Property 'self.addr' not initialized at super.init call
        
        // 1. 先给当前类的存储变量赋值
        self.addr = addr
        // 2. 调用super.init(),给继承的属性赋值;
        // 如果是`convenience initializer`,则调用自身的`designated initializer`,如self.init()
        super.init(age: age)
    }
}

示例二:

class ClassA {
    var name: String?
    
    init() {
        self.name = "no name"
    }
}

class ClassB: ClassA {
    var age: Int
    
    init(age: Int, name: String) {
        // 调用 `super.init()` 之前必须把所有存储属性赋值好
        self.age = age
        super.init()
        // 必须先调用 super.init(),然后才可以对继承过来的属性赋值
        self.name = name
    }
}

let clsB = ClassB(age: 10, name: "人民重重")
print(clsB.age, clsB.name ?? "")
正确顺序:
    1. 先给当前类的存储变量赋值
    1. 调用 super.init(),如果是 便利构造器,则调用自身的 全能初始化方法,如 self.init()
    1. 给继承的属性赋值。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容