Swift类初始化禁止赋值self,为何协议初始化却允许?
为什么Swift类初始化器中禁止
self = someValue,协议扩展却允许? 这个问题的核心要从Swift类的两阶段初始化机制和协议扩展的特殊性说起,我来给你拆解清楚:
一、类初始化器里不能赋值self的原因
Swift对类的初始化有非常严格的规则,核心就是两阶段初始化:
- 第一阶段:从子类到父类,必须依次完成所有存储属性的初始化,确保每个属性都有合法的初始值。在这个阶段,
self处于「不可变」状态——你只能给单个属性赋值,不能直接替换整个实例的引用。 - 第二阶段:所有属性初始化完成后,
self才变得可变,你可以修改属性、调用实例方法,但即便如此,类的初始化器也不允许直接赋值self——因为初始化器的本质职责是创建并初始化新实例,而不是直接复用已有实例的引用。
你写的convenience init(from1 other: MyClass)里直接赋值self = other,编译器报错就是因为:它认为你还没完成当前实例的初始化流程,不允许你跳过初始化步骤直接替换引用。
二、协议扩展能绕过限制的原因
协议扩展的初始化器不受类的两阶段初始化规则约束,主要有两个关键点:
- 协议本身不涉及继承,也没有类那样的初始化流程要求。协议扩展的代码是针对协议类型设计的,编译器不会用类的初始化规则去约束它。
- 当你在协议扩展里写
self = other时,这个初始化器的行为更像一个「静态工厂方法」——它并没有走类的正常初始化流程(比如没有调用init(content:)去初始化content属性),而是直接将当前要创建的实例引用指向了传入的other实例。
你可以验证一下:调用MyClass(from2: foo)后,打印出来的实例和foo是同一个对象,本质上并没有创建新实例,只是复用了已有引用。编译器允许这种操作,是因为它把协议扩展的初始化器当成了「特殊的实例复用逻辑」,而非严格意义上的类初始化流程。
额外提醒:规范的写法
虽然这种协议扩展的方式能工作,但其实不符合Swift初始化的设计意图。如果你的需求是实现一个复制实例的初始化器,更规范的写法是调用类的指定初始化器:
convenience init(from other: MyClass) { self.init(content: other.content) }
这样既遵守了类的初始化规则,也能正确创建新的实例(而非复用已有引用)。
内容的提问来源于stack exchange,提问作者Ky -
相关产品推荐
相关产品推荐

