【问题标题】:Why can't you initialize a protocol that has default implementation?为什么不能初始化具有默认实现的协议?
【发布时间】:2019-11-11 23:06:31
【问题描述】:

我读过In swift, why can't I instantiate a protocol when it has an initialiser?

我的问题集中在为什么编译器不能查看您的默认实现并基于它初始化对象?

protocol ViewModel {
    var radius: Int { get }
    init()
}

extension ViewModel {
    var radius: Int { return 2}
    init() {}
}

let v = ViewModel() // ERROR: 

无法实例化协议类型“ViewModel”

问题1:

为什么 Swift 不允许协议的原始初始化?为什么它必须绑定到具体类型?

我知道它不是一个具体的类型。但是为什么编译器不允许你创建一个只有协议默认值的类型呢?!是不是因为编译器就像嘿听一样,而我可以要么 将您视为接口/协议或实际类型。我不能认为你们两个!?你要么是记忆中的真实存在,要么只是一个蓝图。

如果语言可以检查扩展是否为所有需求提供了实现,那么允许将其初始化为特殊情况是否有意义? (我知道它没有,但想知道的是唯一需要让它工作)或者即使那样这也没有意义。如果是,为什么?

另外我尝试这样做:

protocol ViewModel {
    var radius: Int { get }
    init()
}

extension ViewModel {
    var radius: Int { return 2}
    init() {
        self.init() // Line A: 
    }
}

struct ReallyNothing: ViewModel {}

let v = ReallyNothing()

如果我注释掉 LineA 那么我会得到一个错误

'self.init' 在返回之前不会在所有路径上调用 初始化器

问题2:

为什么?为什么init 必须调用self.init() 看起来有点像递归循环。

【问题讨论】:

  • 协议只是定义了一个接口。这不是具体的。
  • Why does it have to be tied to a concrete type? 因为这是它指定的用途
  • @rmaddy 协议只是定义了一个接口。它不是具体的。 所以呢?
  • 看看In swift, why can't I instantiate a protocol when it has an initialiser?。 TL;DL:协议不是具体类型,所以当你调用Protocol.init() 时,编译器不知道要初始化什么具体类型。它只会知道你要初始化的类型有Protocol描述的接口。
  • @DávidPásztor 我看到了,谢谢。 编译器不知道要初始化什么具体类型 它必须这样做吗?它已经拥有扩展中所需的所有实现细节。编译器如何不必实现默认函数?这有什么不同?为什么编译器不能允许默认实现作为其最基本的一致性?我猜你可能会争辩说编译器无法知道你是否已经实现了它的所有要求。对?所以依赖扩展并不是万无一失的解决方案

标签: swift initialization protocols swift-protocols designated-initializer


【解决方案1】:

协议不是具体类型,因此当您调用Protocol.init() 时,编译器不知道要初始化什么具体类型。它只会知道你要初始化的类型有Protocol描述的接口。

由于内存分配,编译器必须知道具体类型。协议只定义了其符合类型必须具有的所需属性(和方法)的子集,但没有定义其符合类型将具有的全部属性集,因为这实际上因类型而异。因此,如果您能够初始化协议类型,编译器和运行时将不知道为该特定对象分配多少内存,这是一条信息,没有它就无法初始化任何对象。

【讨论】:

  • 为什么协议不能具体初始化为扩展中的协议?
  • @Honey 似乎您误解了这样一个事实,即某些东西具有初始化程序并且运行时实际上可以创建这样的对象。将空初始化器作为协议要求(甚至是它的默认实现)只能保证任何符合标准的类型都具有这样的接口,并且它们所有存储的属性都具有默认值(或者以其他方式初始化,无需注入)。但是,正如我已经说过的,一个空的 init 不包含有关属性类型和数量以及类型的内存需求的任何信息。
  • 因此,运行时实际上不能分配这样的对象。空的 init 只给你,程序员一个契约,任何符合该协议的类型都将有一个不接受输入参数的 init。该 init 的默认实现还允许程序员不以符合类型的方式实现空 init。但是,运行时需要更多信息才能为任何对象分配内存,即所有属性的数量和类型。此信息根本不包含在 init 中。
  • 你能解决我在原问题中提出的第二个问题吗?
  • @Honey 遗憾的是,我很确定为什么会这样,但它不会改变原始问题的答案,因为即使使用第二个示例,也无法初始化协议类型。
【解决方案2】:

这与 ViewModel 是否有init 无关。它与 ViewModel 是什么有关。这是一个协议。

协议不是对象。 没有作为协议的实例。 “实例化”协议意味着什么?没有。您可以创建一个实例:

  • 一个枚举
  • 一个结构
  • 或一类

协议不是这些。您不能创建协议的实例。

ViewModel 是一种协议。所以你不能做一个实例。短语ViewModel() 毫无意义。编译器会告诉你。

【讨论】:

  • 如果语言可以检查扩展是否为所有需求提供了实现,那么允许将其初始化为特殊情况是否有意义? (我知道它没有,但想知道的是唯一需要让它工作)或者即使那样这也没有意义。因为否则我必须创建除了遵循和利用默认实现之外什么都不做的具体类型。
【解决方案3】:

https://docs.swift.org/swift-book/LanguageGuide/Protocols.html

协议是蓝图,不包含实现——所以你不能初始化它们。

一个类同意协议并且必须提供实际的实现。

【讨论】:

  • 协议是蓝图,不包含实现——所以你不能初始化它们extension我确实提供了实现。
  • 我强烈建议您查看 swift 文档的链接。对于初学者,请查看协议名称与类或结构名称的不同方式,然后注意类或结构名称在其定义中选择加入协议。扩展就是这样做的,它们扩展了一个现有的类(一个实际的例子是在一个单独的文件中为给定的协议提供特定的实现)。你永远不会扩展协议本身。
  • @AndyStagg 你的最后一句话是完全错误的。当您想为协议的特定方法提供默认实现以在不同的符合类型之间共享通用功能时,您通常会扩展协议。
  • @DávidPásztor 对不起,我的错误——我以前没有这样用过它们,也没有想过。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-23
  • 2018-12-03
  • 1970-01-01
  • 1970-01-01
  • 2017-05-11
  • 1970-01-01
相关资源
最近更新 更多