编程iOS/Swift 开发者

什么是 Swift 中的初始化委托(Initializer Delegation),指定初始化器和便利初始化器之间的委托规则是如何工作的,这对继承有何影响?

用 Hintsage AI 助手通过面试

答案。

初始化委托—这是内置于 Swift 中的一种类初始化委托系统。在 Swift 中,指定初始化器(设计初始化器)是负责完全初始化所有属性的类主要初始化器,而便利初始化器是辅助性初始化器,简化了使用不同参数集合创建实例的过程。

**问题:**如果允许类及其子类之间的初始化器混乱执行,可能会出现基本类未被完全初始化,或者初始化进行了多次,导致对象无效的情况。

解决方案是严格的规则:

  1. 指定初始化器始终调用超类的指定初始化器。
  2. 便利初始化器始终调用同类的另一个初始化器(指定或其他便利)。
  3. 便利初始化器不能直接调用超类。

代码示例:

class Vehicle { var wheels: Int // 指定初始化器 init(wheels: Int) { self.wheels = wheels } // 便利初始化器 convenience init() { self.init(wheels: 4) } } class Car: Vehicle { var color: String // 指定初始化器 init(wheels: Int, color: String) { self.color = color super.init(wheels: wheels) } // 便利初始化器 convenience init(color: String) { self.init(wheels: 4, color: color) } }

关键特点:

  • 创建具有不同参数集的实例时,代码重复最小化。
  • 支持类的安全继承。
  • 确保对象所有属性的正确初始化。

拔出问题。

问题 1:便利初始化器能否通过 super.init 直接调用超类的初始化器?

不,便利初始化器总是将初始化委托给当前类的另一个初始化器,后者可以调用超类的指定初始化器。

问题 2:如果不在子类中实现所需的初始化器,会发生什么?

如果超类有所需的初始化器,则必须在每个子类中重写它(使用 required),否则编译器将抛出错误。

问题 3:便利 init 和便利所需 init 之间有什么区别?

便利所需初始化器 是必需的,如果超类中将特定便利初始化器声明为所需,以确保在继承层次中支持初始化。

常见错误和反模式

  • 错误的便利/初始化调用链和属性初始化的破坏。
  • 忘记在子类中实现所需的初始化器。
  • 在便利初始化器中调用 super.init,这是无效的。

生活中的案例

消极案例

开发者在便利初始化器中调用了 super.init。代码之所以能编译是因为缺少某些限制,但在执行时发生了错误:并非所有对象属性都被初始化。

优点:

  • 代码对于新手来说看起来清晰,因为它类似于其他语言中的实现。

缺点:

  • 遗留模式导致继承中的子类未正确初始化,应用程序崩溃。
  • 需要重构。

积极案例

使用结构清晰的指定和便利初始化器,明确彼此调用。逻辑严格遵循 Swift 的规则,确保初始化始终清晰且正确。

优点:

  • 容易扩展和维护类层次结构。
  • 消除了重复代码。
  • 简化了测试。

缺点:

  • 需要仔细文档化大的继承层次中的初始化器。