Swift两阶段初始化
Swift两阶段初始化

Swift两阶段初始化

“两阶段初始化”(Two-Phase Initialization)是 Swift 中确保类的所有属性在使用前被完全初始化的一种机制。它适用于所有类(class)类型,尤其是存在继承关系时非常重要。

什么需要两阶段初始化?

因为 Swift 允许类有继承关系,子类可能在初始化时要依赖父类的内容,必须确保:

1、所有属性都有值;

2、初始化顺序安全;

3、父类部分初始化完成之前,子类不能访问自己或父类的方法/属性。

两阶段初始化过程

第一阶段:初始化自身所有属性

子类先为自己的非可选属性设定值。

然后调用 super.init(…),让父类也完成初始化。

在第一阶段完成之前,不能使用 self,包括访问属性、调用方法等。

例如:

class ScreenshotOverlayView: NSView {
    var selectionLayer: CAShapeLayer
    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)    // 先初始化父类属性,会提示:Property 'self.selectionLayer' not initialized at super.init call
        selectionLayer = CAShapeLayer() 
    }
}

在NSView子类代码中,重写构造器,因为先初始化父类属性,就会报错:

Property 'self.selectionLayer' not initialized at super.init call

这是Swift的初始化规则,要求必须在super.init()之前,完成子类中非可选的属性初始化。

因为selectionLayer变量是非可选类型:

var selectionLayer: CAShapeLayer // 非可选,没有默认值

因此,违反了Swift的两阶段初始化规则 — 所有非可选属性必须在调用 super.init 之前初始化完毕。

第二阶段:使用 self(属性、方法都可以使用)

父类初始化完成,回到子类构造器。

此时可以自由访问 self、使用方法、设置属性等。

class ScreenshotOverlayView: NSView {
    var selectionLayer: CAShapeLayer
    override init(frame frameRect: NSRect) {
        selectionLayer = CAShapeLayer() // 完成子类非可选类型初始化
        super.init(frame: frameRect)    // 完成父类初始化
        selectionLayer.fillColor = .white   // 使用 self 修改属性和方法
    }
}

使用示例

1、两阶段初始化

class Animal {
    var name: String

    init(name: String) {
        self.name = name      // 初始化自己的属性
        print("Animal init")
    }
}

class Dog: Animal {
    var breed: String

    init(name: String, breed: String) {
        self.breed = breed    // 初始化子类自己的属性(第一阶段)
        super.init(name: name) // 调用父类 init(仍然在第一阶段)
        print("Dog initialized with name: \(self.name) and breed: \(self.breed)") // 现在可以使用 self(第二阶段)
    }
}

总结

Swift 的初始化顺序非常严格,尤其是涉及到类继承(如 NSView)时:

1、所有非可选的属性都必须在调用 super.init() 之前初始化,或者在声明时就提供默认值;

2、在super.init()之前,不可以调用方法或者访问为初始化的属性,也不可以传递self到其他地方。

override init(frame frameRect: NSRect) {
    complete()  // 在使用super.init之前不能调用方法,否则报错:'self' used in method call 'complete' before 'super.init' call
    super.init(frame: frameRect)
}

3、推荐使用属性默认值 + 简洁构造器,减少出错空间。

相关文章

1、Swift完整初始化覆盖链:https://fangjunyu.com/2025/07/31/swift-%e5%ae%8c%e6%95%b4%e5%88%9d%e5%a7%8b%e5%8c%96%e8%a6%86%e7%9b%96%e9%93%be/

2、SwifUI @State初始化报错原因与修复方法:https://fangjunyu.com/2024/12/03/swifui-state%e5%88%9d%e5%a7%8b%e5%8c%96%e6%8a%a5%e9%94%99%e5%8e%9f%e5%9b%a0%e4%b8%8e%e4%bf%ae%e5%a4%8d%e6%96%b9%e6%b3%95/

3、@State的初始化机制和Swift的编译器规则:https://fangjunyu.com/2024/11/25/state%e7%9a%84%e5%88%9d%e5%a7%8b%e5%8c%96%e6%9c%ba%e5%88%b6%e5%92%8cswift%e7%9a%84%e7%bc%96%e8%af%91%e5%99%a8%e8%a7%84%e5%88%99/

4、Swift私有化初始化方法:https://fangjunyu.com/2024/10/19/swift%e7%a7%81%e6%9c%89%e5%8c%96%e5%88%9d%e5%a7%8b%e5%8c%96%e6%96%b9%e6%b3%95/

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注