如果我宣布
public class A: NSObject {
public class X { }
public init?(x: X? = nil) { }
}
一切都很好。当像let a = A()
一样使用它时,初始化程序会按预期调用。
现在,我希望嵌套的类X
是私有的,参数化的init
也是如此(必须是)。但是,一个简单的init?()
应该像以前一样公开。所以我写了
public class B: NSObject {
private class X { }
private init?(x: X?) { }
public convenience override init?() { self.init(x: nil) }
}
但这会导致init?()
初始化程序出错:可用的初始化程序' init()'无法覆盖不可用的初始值设定项且重写的初始值设定项为public init()
中的NSObject
。
为什么我可以有效地声明初始化程序A.init?()
而不会发生冲突但不能B.init?()
?
奖金问题:为什么我不允许使用可用的初始化程序覆盖不可用的初始化程序?相反的是合法的:我可以使用不可用的覆盖可用的初始化程序,这需要使用强制super.init()!
,从而引入运行时错误的风险。对我来说,让子类具有可用的初始化器感觉更加明智,因为功能的扩展会带来更多的失败机会。但也许我在这里遗漏了一些东西 - 非常感谢。
答案 0 :(得分:9)
这就是我为我解决问题的方法:
我可以声明
public convenience init?(_: Void) { self.init(x: nil) }
并像
一样使用它let b = B(())
甚至
let b = B()
- 这是合乎逻辑的,因为它的签名是(种类)不同,所以没有覆盖在这里。只使用Void
参数并在通话中省略它感觉有点奇怪......但我认为最终证明了手段的合理性。 : - )
答案 1 :(得分:2)
经过一番摆弄后,我想我明白了。让我们考虑一个需要这个初始化器的协议和一个实现它的类:
protocol I {
init()
}
class A : I {
init() {}
}
这给出了错误:“初始化程序要求'init()'只能由非最终类'A'中的required
初始化程序来满足。这是有道理的,因为您总是可以声明A
的子类不继承该初始化器:
class B : A {
// init() is not inherited
init(n: Int) {}
}
因此,我们需要在A
required
中创建初始值设定项:
class A : I {
required init() {}
}
现在,如果我们查看NSObject
界面,我们可以看到初始值设定项不 required
:
public class NSObject : NSObjectProtocol {
[...]
public init()
[...]
}
我们可以通过继承它,添加不同的初始化程序并尝试使用正常的初始化程序来确认这一点:
class MyObject : NSObject {
init(n: Int) {}
}
MyObject() // Error: Missing argument for parameter 'n:' in call
现在出现了一个奇怪的事情:我们可以扩展NSObject
以符合I
协议,即使它不需要这个初始值设定项:
extension NSObject : I {} // No error (!)
老实说,我认为这可能是一个错误或ObjC互操作的要求(编辑:这是一个错误,已在最新版本中修复)。不应该出现此错误:
extension I {
static func get() -> Self { return Self() }
}
MyObject.get()
// Runtime error: use of unimplemented initializer 'init()' for class '__lldb_expr_248.MyObject'
现在回答你的实际问题:
在你的第二个代码示例中,编译器是正确的,因为你不能使用可用的初始化程序覆盖不可用的代码。
在第一个中,你实际上并没有覆盖初始化器(也没有override
关键字),而是声明一个新的,不能继承另一个。
现在我写了这么多,我甚至不确定我的答案的第一部分与你的问题有什么关系,但不管怎么说,找到一个错误很好。
我建议你这样做:
public convenience override init() { self.init(x: nil)! }
另请查看Swift参考的Initialization
section。