【问题标题】:Why must Protocol defaults be implemented via Extensions in Swift?为什么必须通过 Swift 中的扩展来实现协议默认值?
【发布时间】:2018-01-22 02:55:26
【问题描述】:

在 Swift 中,您不能在协议定义本身中定义函数或属性的默认实现,即:

protocol Container {
    //These are fine
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get set }
    subscript(i: Int) -> Item { get }

    //These are not fine
    var defaultValue: Int = 10
    mutating func messWithCount(){
        self.count *= 10
    }
}    

extension Container {
    //This is fine though
    mutating func messWithCount(){
        self.count *= 10
    }
}

但是您可以通过扩展来做到这一点(虽然扩展不支持存储属性,但它们支持函数和计算的 - 尽管存储属性问题可能是 worked around)。

这背后的解释是什么?作为补充,optional func 只有在我们将 Protocol 和 func 都标记为 @objc 并因此使其无法用于 Structs/Enums(基于 Value 而非 Reference)时才能实现的解释是什么?

编辑:在扩展示例中添加

【问题讨论】:

  • 协议声明中的默认实现是不可能的,这并没有真正的原因;区别只是语法上的。因此,我认为实施(如果有的话)不会是一个高优先级。不过它是included in the generics manifesto,因此可能会考虑用于该语言的更高版本。

标签: swift reference protocols extension-methods


【解决方案1】:

@optional 指令是一个仅限 Objective-C 的指令,尚未翻译成 Swift。这并不意味着您不能在 Swift 中使用它,而是您必须首先使用 @objc 属性将您的 Swift 代码公开给 Objective-C。

请注意,向 Obj-C 公开只会使协议对 Swift 和 Obj-C 中的类型都可用,这不包括例如 Struct,因为它们仅在 Swift 中可用!

为了回答您的第一个问题,协议是在这里定义而不是实现:

协议定义了方法、属性和其他要求的蓝图 [...]

因此,实现应该由符合它的类/结构/枚举提供:

然后该协议可以被类、结构或枚举采用,以提供这些要求的实际实现

这个定义也确实适用于我们在日常生活中使用的协议。以写论文的协议为例:

PaperProtocol 将论文定义为具有以下非零变量的文档:

  • 简介
  • 章节
  • 结论

引言、章节和结论的内容取决于实现它们的人(作者),而不是协议。

当我们查看扩展的定义时,我们可以看到它们在这里添加(实现)新功能:

扩展为现有的类、结构、枚举或协议类型添加新功能。这包括扩展您无权访问原始源代码的类型的能力。

因此,扩展协议(这是允许的)使您可以添加新功能,从而提供已定义方法的默认实现。这样做将作为上面讨论的@optional 指令的唯一替代方案。

更新:

虽然在 Switch 中为协议函数提供默认实现确实使其“可选”,但它与在 Objective-C 中使用 @optional 指令基本上不同。

在 Objective-C 中,未实现的可选方法根本没有实现,因此调用它会导致崩溃,因为它不存在。因此,在调用它之前必须检查它是否存在,这与带有扩展默认值的 Swift 不同,您可以在默认实现存在时安全地调用它。

Obj-C 中的可选项会这样使用:

NSString *thisSegmentTitle;
// Verify that the optional method has been implemented
if ([self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]) {
    // Call it
    thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index];
} else {
    // Do something as fallback
}

它的 Swift 对应的扩展默认是:

let thisSegmentTitle = self.dataSource.titleForSegmentAtIndex(index)

【讨论】:

  • 所以这种现象是由 Swift(因此是 Chris Lattner)如何定义协议和扩展引起的,而不是一些隐藏的实现细节? Swift 的文档确实强调了 Extension default v @optional 是不一样的 - (扩展提供的默认实现的协议要求与可选协议要求不同)。我个人觉得为这样的事情扩展有点笨拙,可能是因为我太习惯 Java 的接口风格
  • @Mercato 我用可选和扩展默认值之间的主要区别更新了我的答案,因为评论太长了!
猜你喜欢
  • 1970-01-01
  • 2016-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-02
  • 1970-01-01
相关资源
最近更新 更多