【问题标题】:Calling a protocol extension initializer调用协议扩展初始化程序
【发布时间】:2018-08-13 21:13:11
【问题描述】:

我正在尝试构建一组共享通用初始化代码的类。除了继承,我认为协议将是要走的路。虽然协议和协议扩展对我来说适用于实例和静态方法,但我在使用初始化器时遇到了一些麻烦。

假设我们有这个协议:

protocol CloudServiceWrapper {

    static var isConfigured: Bool { get }

    init()

    func initialize()

}

现在假设我们要在协议扩展中为 isConfiguredinit() 添加默认实现:

extension CloudServiceWrapper {

    static var isConfigured: Bool {
        get {
            return true
        }
    }

    init() {
        print("Initializing generic CloudServiceWrapper")
        self.init()
        if Self.isConfigured {
            initialize()
        }

    }

}

最后,让我们有一个类实现这个协议并尝试从它的默认实现中受益:

class OneDriveWrapper: CloudServiceWrapper {

    required init() {
        // CloudServiceWrapper() // error: protocol type 'CloudServiceWrapper' cannot be instantiated
        // self = CloudServiceWrapper.init() // error: cannot assign to value: 'self' is immutable
        // super.init() // error: 'super' members cannot be referenced in a root class
        // (self as CloudServiceWrapper).init() // error: static member 'init' cannot be used on instance of type 'CloudServiceWrapper'
        print("Initializing OneDriveWrapper")
    }

    func initialize() {
        print("Done")
    }

}

在尝试构建OneDriveWrapper 类的新实例时,我根本找不到既调用类的初始化程序又调用默认协议实现的方法。并且不可能在 OneDriveWrapper 类中省略 init(),因为它是协议定义所必需的,并且似乎不被认为是通过协议扩展“实现”的。

事实上,更广泛地说,我找不到任何方法来显式调用协议扩展的初始化程序,尽管我知道实例方法是可能的。

我做错了什么?您知道将类的初始化程序和协议扩展的初始化程序结合起来的任何方法吗?我应该回到类继承而不是协议和扩展吗?

谢谢!

【问题讨论】:

  • 我认为你只能使用子类。

标签: ios swift swift-protocols protocol-extension


【解决方案1】:

protocol 中的initrequired,因此必须显式实现,即不能使用默认实现。

至于“显式调用协议扩展的初始化器”,您不能实例化协议类型。

我建议为此使用继承。

【讨论】:

  • 谢谢。 protocol 中的initrequired 非常清楚。
【解决方案2】:

Public default init in protocol 是一个类似的问题。

尽管协议提供了默认实现,为什么一个类需要一个 init?答案是它没有提供默认实现,而只是另一个 init,它需要类的 init() 这里存在。

由于您的协议不能确定覆盖使用它的类的所有成员,因此您在协议中声明的任何初始化程序都需要将类的“未知”成员的初始化委托给该类提供的另一个初始化程序本身。 Source

但是,您的协议初始化可能被类 init() 遮蔽,因此您将无法使用它。也许添加一个参数到类初始化器/协议所需的初始化器或一个参数到协议扩展ìnit()。如果协议扩展 init 没有被类隐藏,您将能够使用它来初始化类。

init 是必需的,因为该类可以是子类并且初始化器不一定是继承的,请参阅inheritance docinitialization doc under Initializer Inheritance and Overriding。如果不需要 init,则子类将不再遵守协议,这是不可取的,因此在协议中设置的 init 仅由所需的初始化程序填充。

注意:在required init() 中,您尝试调用另一个初始化器,但我认为除了初始化父级之外这是不可能的,因为required init() 是指定的初始化器,不能在同一个类中链接,只有便利初始化器可以链接同班。

您可以将协议扩展 ìnit 视为便利初始化程序,因为 init 需要调用另一个初始化程序,就像所有其他便利初始化程序对 self.init(...) 所做的那样。

【讨论】:

    【解决方案3】:

    这个答案没有指出 OP 的核心问题。留在这里只是为了一个记录


    你完全错了。协议不是超类。

    协议的默认实现只是在您不实现协议所需方法时使用的默认值。您不能以任何方式直接调用协议的默认实现。

    协议不是超类的简单替换。根据协议改变你的思维方式。

    尽管我知道实例方法是可能的。

    你弄错了什么。即使是方法,也不能调用协议的默认实现。

    【讨论】:

    • 感谢您的回复。然而,当你说你不能调用协议的默认实现,即使它是一个方法:这不是真的,它实际上是可能的。示例见此处:stackoverflow.com/a/34777469/145997
    • 我会警惕告诉人们他们(或正在做的事情)“完全错误”。这不是很好;即使是这样,也不会给你留下太多犯错而不丢脸的空间。
    • @Romain,这表明您误解了链接文章的含义。当方法声明被移除时,扩展中的方法不是默认实现。
    • @zneak,感谢您的建议。我不明白你在不是很好之后的意思,但我发现我的回答只是坚持你不能以任何方式直接调用协议的默认实现 ,并省略了 OP 的许多其他方面或利益。
    • 没有冒犯 OOPer。我必须承认,当我第一次阅读@zneak 的评论时,我感到有点欣慰,因为我不是唯一一个发现你的帖子有点粗糙(即使是正确的)的人。但这没关系,因为你是对的,让我知道我确实弄错了一些东西。另外,感谢@zneak 的干预!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-06
    • 1970-01-01
    • 2020-05-17
    • 2015-09-12
    • 1970-01-01
    • 1970-01-01
    • 2011-07-11
    相关资源
    最近更新 更多