我不得不承认,“重写可失败的初始化程序”部分相当混乱。
以下示例应阐明这种情况:
假设,您有一个带有可失败初始化程序的基类:
class Base {
let name: String
init?(name: String) {
guard !name.isEmpty else {
return nil
}
self.name = name
}
}
请注意,失败的初始化程序会返回一个 Optional。
这里,初始化器要求您传递一个非空字符串,否则初始化器“失败” - 也就是说,它返回一个值为nil(分别为.None)的可选项。
现在,让我们定义一个派生自Base 的类。在第一个版本中,这不会编译,艰难!
class Derived: Base {
let age: Int
init(name: String, age: Int) {
self.age = age
super.init(name: name) //<- error
}
}
编译器发出以下错误:
error: a non-failable initializer cannot chain to failable initializer 'init(name:)' written with 'init?'
super.init(name: name)
^
这里的问题是,子类的非失败初始化器委托给失败的基类初始化器。
我们有两个选项来纠正这个问题:
1。强制解开可失败的初始化程序:
class Derived: Base {
let age: Int
init(name: String, age: Int) {
self.age = age
super.init(name: name)! // <- force unwrap
}
}
此解决方案的注意事项是,如果您将空的 name 传递给子类初始化程序,例如
let derived = Derived(name: "", age: 12)
在尝试从基类初始化程序中强制解包可选时会导致致命错误:
fatal error: unexpectedly found nil while unwrapping an Optional value
2. 使子类初始化程序也失败:
class Derived: Base {
let age: Int
init?(name: String, age: Int) { // use failable initialiser
self.age = age
super.init(name: name) // <- propagate the failure with init?
}
}
此解决方案只是将 nil 结果从基类初始化程序传播到调用者 - 这使得调用者负责适当地处理可选选项。