为什么协议默认值必须通过 Swift 中的扩展来实现?

Why must Protocol defaults be implemented via Extensions in Swift?

在 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
    }
}

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

这背后的解释是什么?作为补充,只有当我们将协议和函数都标记为 @objc 并因此使其无法用于 Structs/Enums 时,optional func 才可实现的解释是什么(这是值而不是引用基于)?

编辑:在扩展示例中添加

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

请注意,暴露给 Obj-C 只会使协议可用于 Swift 和 Obj-C 中的类型,这不包括例如结构,因为它们仅在 Swift 中可用!

回答你的第一个问题,这里的协议是用来定义而不是实现的:

A protocol defines a blueprint of methods, properties, and other requirements [...]

因此,实现应由符合它的 class/stuct/enum 提供:

The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements

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

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

  • 简介
  • 章节
  • 结论

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

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

Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code.

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

更新:

虽然在 Switch 中为协议函数提供默认实现确实使它成为 "optional",但它与在 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)