Swift:"可用的初始化程序' init()'不能覆盖不可用的初始化程序"与默认参数

时间:2016-07-11 16:00:04

标签: swift initializer failable

如果我宣布

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()!,从而引入运行时错误的风险。对我来说,让子类具有可用的初始化器感觉更加明智,因为功能的扩展会带来更多的失败机会。但也许我在这里遗漏了一些东西 - 非常感谢。

2 个答案:

答案 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