Swift 构造过程

Swift 的构造过程(Initialization)是创建类、结构体、枚举实例的关键步骤。它的目标是确保实例的所有存储属性都被正确初始化,从而保证对象安全可用。

Swift 的构造过程设计严谨、安全,遵循 “两阶段初始化” 规则,防止未初始化属性被访问。


一、构造过程总览

类型构造器写法是否支持继承
structinit(...)不支持
classinit(...) / convenience init(...)支持
enuminit(...)不支持

二、存储属性初始化方式

所有存储属性必须在构造完成前被初始化:

struct Point {
    var x: Double
    var y: Double = 0.0        // 默认值
}

class Person {
    var name: String           // 必须在 init 中初始化
    let age: Int = 18          // 常量 + 默认值
}

三、构造器类型

1. 指定构造器(Designated Initializer)

  • 必须初始化本类所有存储属性
  • 必须调用父类的指定构造器
class Vehicle {
    var wheels: Int

    init(wheels: Int) {           // 指定构造器
        self.wheels = wheels
    }
}

class Car: Vehicle {
    var brand: String

    init(brand: String, wheels: Int) {  // 指定构造器
        self.brand = brand
        super.init(wheels: wheels)      // 调用父类指定构造器
    }
}

2. 便利构造器(Convenience Initializer)

  • 必须用 convenience 标记
  • 必须横向调用本类的指定构造器
  • 不能调用 super.init
class Car: Vehicle {
    var brand: String

    init(brand: String, wheels: Int) {
        self.brand = brand
        super.init(wheels: wheels)
    }

    // 便利构造器
    convenience init(brand: String) {
        self.init(brand: brand, wheels: 4)
    }
}

let car = Car(brand: "Tesla")  // 使用便利构造器

四、两阶段初始化(Two-Phase Initialization)

确保安全,防止未初始化属性被使用

阶段 1:初始化本类属性

self.property = value

阶段 2:调用父类构造器 + 自定义逻辑

super.init(...)
self.method()  // 安全调用

安全检查规则(编译器强制)

规则说明
1. 子类必须先初始化自己的属性self.x = 1super.init() 之前
2. 必须调用父类指定构造器super.init(...)
3. 不能在 super.init 前调用实例方法除非父类已初始化
4. 不能在 super.init 前访问 self 作为参数防止未完成初始化
class BadExample: Vehicle {
    var model: String

    init() {
        self.model = "Unknown"
        super.init(wheels: 4)
        // 正确顺序
    }

    // 错误示例(编译失败)
    /*
    init() {
        super.init(wheels: 4)
        self.model = "Unknown"  // 错误:super.init 后才能访问 self
    }
    */
}

五、自动继承构造器

如果子类没有定义任何指定构造器,会自动继承父类的所有指定构造器

class Bike: Vehicle { }

let bike = Bike(wheels: 2)  // 自动继承

一旦子类定义了任意指定构造器,就不再自动继承


六、可失败构造器(Failable Initializer)

用于可能构造失败的场景,返回 Self?

struct Temperature {
    var celsius: Double

    init?(fahrenheit: Double) {
        guard fahrenheit >= -459.67 else { return nil }  // 绝对零度
        self.celsius = (fahrenheit - 32) * 5 / 9
    }
}

if let temp = Temperature(fahrenheit: 98.6) {
    print(temp.celsius)  // 37.0
}

let invalid = Temperature(fahrenheit: -1000)  // nil

enum 常用:

enum Status: Int {
    case ok = 200, notFound = 404
    init?(code: Int) {
        switch code {
        case 200: self = .ok
        case 404: self = .notFound
        default: return nil
        }
    }
}

七、必要构造器(Required Initializer)

强制子类必须实现。

class Base {
    required init() {
        print("Base 初始化")
    }
}

class Child: Base {
    required init() {  // 必须实现
        super.init()
    }
}

class GrandChild: Child {
    required init() {  // 必须传递
        super.init()
    }
}

八、默认构造器(Default Initializer)

当所有属性都有默认值时,自动获得:

struct Point {
    var x = 0.0
    var y = 0.0
}

let p = Point()  // 自动无参构造器

自定义 init 后,默认构造器消失


九、值类型的构造器委托

结构体和枚举支持构造器之间互相调用

struct Size {
    var width: Double
    var height: Double

    init(width: Double, height: Double) {
        self.width = width
        self.height = height
    }

    init(square: Double) {
        self.init(width: square, height: square)  // 委托
    }
}

let s1 = Size(square: 10)

十、deinit(析构过程)

与构造对应,释放资源。

class FileHandler {
    let name: String

    init(name: String) {
        self.name = name
        print("打开文件: \(name)")
    }

    deinit {
        print("关闭文件: \(name)")
    }
}

var file: FileHandler? = FileHandler(name: "data.txt")
file = nil
// 打开文件: data.txt
// 关闭文件: data.txt

构造过程流程图

┌─────────────────────┐
│   子类便利构造器      │
└────────┬────────────┘
         ↓
┌─────────────────────┐
│   子类指定构造器      │ → 初始化子类属性
└────────┬────────────┘
         ↓
┌─────────────────────┐
│   super.init(...)    │ → 调用父类指定构造器
└────────┬────────────┘
         ↓
┌─────────────────────┐
│   父类构造完成       │
└────────┬────────────┘
         ↓
┌─────────────────────┐
│   实例完全可用       │ → 可调用方法、访问 self
└─────────────────────┘

最佳实践总结

场景推荐
简单数据struct + 默认值
复杂对象class + 指定构造器
提供简化入口convenience init
可能失败init?
强制实现required init
清理资源deinit

常见面试题

  1. 什么是两阶段初始化?
    → 阶段1:初始化本类属性 → 阶段2:调用 super.init
  2. 为什么 convenience init 不能调用 super.init
    → 必须通过本类指定构造器统一调用父类。
  3. 子类定义了构造器后,父类构造器还能用吗?
    → 不能,除非子类没有定义指定构造器
  4. 可失败构造器返回 nil 时,deinit 会执行吗?
    → 不会,对象未创建成功。
  5. required initoverride init 的区别?
    required:强制子类实现
    override:重写父类构造器

小技巧

// 1. 构造器中使用属性包装器
@propertyWrapper
struct Positive {
    var value: Int
    init(wrappedValue: Int) {
        self.value = max(0, wrappedValue)
    }
}

struct Score {
    @Positive var value: Int
    init(value: Int) {
        self.value = value  // 自动包装
    }
}

// 2. 延迟初始化(配合 lazy)
class ImageLoader {
    lazy var image: UIImage = {
        return UIImage(named: "default")!
    }()
}

高级话题(可继续提问)

  • 构造器与 Codable 自动解码
  • 构造器与依赖注入(DI)
  • 构造器失败与 fatalError vs init?
  • SwiftUI 中 @State 的构造过程
  • 反射构造(NSClassFromString

需要完整登录系统构造流程JSON 解析构造器实战,或与工厂模式结合?欢迎继续提问!

文章已创建 2481

发表回复

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

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部