Swift struct default 初始化器和 memberwise 初始化器

27 阅读2分钟

一个 struct 如果没有定义任何自定义初始化器,编译器会生成 memberwise 初始化器。我们看代码

struct S {
    var x: Int
}

结构 S 只有一个存储属性 x,编译器会生成 memberwise 初始化器。

init(x: Int) {
    self.x = x
}

如果属性 x 有默认值,

struct S {
    var x: Int = 6
}

编译器会生成 default 初始化器 init() 和 memberwise 初始化器 init(x: Int = 6)。

init() {
}

init(x: Int = 6) {
    self.x = x
}

生成 default 初始化器 init() 的条件是 struct 为所有属性提供了默认值,并且没有定义任何自定义初始化器。default 初始化器简单地创建一个所有属性值都设置为它们默认值的实例。

结构 S 及其属性 x 的访问级别都是 internal,我们把 x 的访问级别改为 private,

struct S {
    private var x: Int
}

此时

var s = S(x: 12)

会报错,'S' initializer is inaccessible due to 'private' protection level,因为生成的 memberwise 初始化器 init(x: Int) 访问 private 属性 x,它也为 private,只能在 struct 内部访问。

struct S {
    private var x: Int
    
    static func create(x: Int) -> S {
        let s = S(x: x)
        return s
    }
}

var s = S.create(x: 12)

静态方法 create(x: Int) 能访问 private memberwise 初始化器,上面代码能正常编译。

再看属性 x 有默认值的情况,

struct S {
    private var x: Int = 6
}

编译器会生成 default 初始化器 init() 和 private memberwise 初始化器 init(x: Int = 6)。

init() {
}

private init(x: Int = 6) {
    self.x = x
}

由于 default 初始化器 init() 未访问属性 x,它访问级别仍然是 internal。因此,

var s = S()

能编译通过。

var s = S(x: 12)

报错,Argument passed to call that takes no arguments。

然而

struct S {
    private var x: Int = 6
    
    static func create(x: Int) -> S {
        let s = S(x: x)
        return s
    }
}

能编译通过。

以上4种情况总结如下:

以上都只有一个属性,如果 struct 有多个属性,上述规则仍然成立。

struct S {
    private var x: Int = 6
    var y: Int
}

编译器生成 memberwise 初始化器,

private init(x: Int = 6, y: Int) {
    self.x = x
    self.y = y
}

只要有一个属性是 private,则 memberwise 初始化器就是 private。

var s = S(y: 6)

报错,'S' initializer is inaccessible due to 'private' protection level。

如果 y 有默认值,

struct S {
    private var x: Int = 6
    var y: Int = 8
}

相信大家已经猜到了,生成 default 初始化器 init() 和 memberwise 初始化器 init(x: Int = 6, y: Int = 8)。

init() {
}

private init(x: Int = 6, y: Int = 8) {
    self.x = x
    self.y = y
}

init() 访问级别是 internal,init(x: Int = 6, y: Int = 8) 访问级别是 private。

var s = S()

能编译通过。

struct S {
    private var x: Int = 6
    var y: Int = 8
    
    static func create(x: Int, y: Int) -> S {
        let s = S(x: 12, y: 12)
        return s
    }
}

能编译通过。

以上代码在 Xcode 26.3 验证。不过,在不久的将来,memberwise 初始化器的规则会改变,

var s = S(y: 6)

不会报错,因为提案 SE-0502 已实现。