Swift 的构造过程(Initialization)是创建类、结构体、枚举实例的关键步骤。它的目标是确保实例的所有存储属性都被正确初始化,从而保证对象安全可用。
Swift 的构造过程设计严谨、安全,遵循 “两阶段初始化” 规则,防止未初始化属性被访问。
一、构造过程总览
| 类型 | 构造器写法 | 是否支持继承 |
|---|---|---|
struct | init(...) | 不支持 |
class | init(...) / convenience init(...) | 支持 |
enum | init(...) | 不支持 |
二、存储属性初始化方式
所有存储属性必须在构造完成前被初始化:
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 = 1 在 super.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:初始化本类属性 → 阶段2:调用super.init - 为什么
convenience init不能调用super.init?
→ 必须通过本类指定构造器统一调用父类。 - 子类定义了构造器后,父类构造器还能用吗?
→ 不能,除非子类没有定义指定构造器。 - 可失败构造器返回
nil时,deinit会执行吗?
→ 不会,对象未创建成功。 required init和override 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)
- 构造器失败与
fatalErrorvsinit? - SwiftUI 中
@State的构造过程 - 反射构造(
NSClassFromString)
需要完整登录系统构造流程、JSON 解析构造器实战,或与工厂模式结合?欢迎继续提问!