从常规方法调用协议默认实现


82

我想知道是否有可能实现这样的目标。
我有一个这样的游乐场:

protocol Foo {
    func testPrint()
}

extension Foo {
    func testPrint() {
        print("Protocol extension call")
    }
}

struct Bar: Foo {
    func testPrint() {
        // Calling self or super go call default implementation
        self.testPrint()
        print("Call from struct")
    }
}


let sth = Bar()
sth.testPrint()

我可以在中提供默认实现,extension但是如果Bar需要默认实现中的所有内容以及其他内容,该怎么办?
它在某种程度上类似于es中的调用super.方法,class可以满足实现每个属性等的要求structs


我会使用Foo.testPrint(self)()-问题是由于分段错误(在7.0 GM和7.1 beta上都经过测试)而失败
Antonio

1
这就是你提出😯一个奇怪的建筑
cojoj

4
每个实例方法都是将实例作为其第一个参数的静态咖喱方法
Antonio

但是,我尝试删除扩展名,并且它引发了相同的细分错误。也许这是不应该的工作与协议
安东尼

嗯,可惜我必须在代码中重复一遍,但是可以通过使用默认实现轻松解决此问题...
cojoj 2015年

Answers:


90

我不知道您是否还在寻找答案,但是要做的方法是从协议定义中删除函数,将对象转换为对象Foo,然后在其上调用方法:

protocol Foo { 
    // func testPrint() <- comment this out or remove it
}

extension Foo {
    func testPrint() {
        print("Protocol extension call")
    }
}

struct Bar: Foo {
    func testPrint() {
        print("Call from struct")
        (self as Foo).testPrint() // <- cast to Foo and you'll get the  default
                                  //    function defined in the extension
    }
}

Bar().testPrint()

// Output:    "Call from struct"
//            "Protocol extension call"

由于某种原因,它仅在函数未声明为协议的一部分但在协议的扩展中定义时才起作用。去搞清楚。但这确实有效。


1
天啊!我不敢相信他们会强迫您将功能从协议中删除!好答案,谢谢!
天行者'16

5
对我来说,这似乎是一个错误,仅通过投射相同的实例就不可能获得不同的方法实现。
MANIAK_dobrii '16

15
这实际上极大地改变了协议+扩展的语义。如果将声明放在协议之外,则在以符合协议的类型调用函数时将获得静态分派-这就是为什么可以强制转换并从扩展中获取实现。如果将声明添加到协议,则将动态调度对函数的调用。
Thorsten Karrer

2
这仅在Foo协议不从任何其他协议继承的情况下才有效。
iyuna

4
这不是导致无限循环吗?
stan liu

9

好吧,您可以创建一个符合协议的嵌套类型,实例化它,然后在该类型上调用该方法(没关系,因为协议扩展内部的实现无法引用它,因此您无法访问类型的数据)。但这不是我所说的优雅的解决方案。

struct Bar: Foo {
    func testPrint() {
        // Calling default implementation
        struct Dummy : Foo {}
        let dummy = Dummy()
        dummy.testPrint()
        print("Call from struct")
    }
}

1
它看起来是正确的,现在(由苹果公司证实),唯一的可能......我会提交功能雷达为这一个,因为它可能是有用的👌
cojoj

4

感谢您的帖子!如果将函数定义放在协议中,则当将该对象强制转换为协议时,它只会看到该对象的函数版本,并且由于您在内部调用它,因此可以获取Apple的新地址。

我确实尝试过这样的版本:

import UIKit
protocol MyProc
{
}

protocol MyFuncProc
{
    func myFunc()
}

extension MyProc
{
    func myFunc()
    {
        print("Extension Version")
    }
}

struct MyStruct: MyProc, MyFuncProc
{
    func myFunc()
    {
        print("Structure Version")
        (self as MyProc).myFunc()
    }
}

(MyStruct() as MyFuncProc).myFunc()

输出为:

Structure Version
Extension Version

3

如果你的协议具有associatedTypeSelf要求,那么中投将无法正常工作。要变通解决此问题,请创建一个“影子”默认实现,常规默认实现和符合性类型都可以调用该默认实现。

protocol Foo { 
    associatedType Bar
}

extension Foo {
    func testPrint() {
        defaultTestPrint()
    }
}

fileprivate extension Foo { // keep this as private as possible
    func defaultTestPrint() {
        // default implementation
    }
}

struct Bar: Foo {
    func testPrint() {
        // specialized implementation
        defaultTestPrint()
    }
}

不能再同意您的意见。defaultXX()比其他答案更具表现力和可读性。
DawnSong

而且我认为Amin Madani改善了您的答案。
DawnSong

2

您如何看待这种解决方案?

protocol Foo {
    func testPrint()
}

extension Foo {
    func testPrint() {
        defaultTestPrint()
    }

    func defaultTestPrint() {
        print("Protocol extension call")
    }
}

struct Bar: Foo {
    func testPrint() {
        // Calling self or super go call default implementation
        defaultTestPrint()
        print("Call from struct")
    }
}


let sth = Bar()
sth.testPrint()

这是答案吗?
安潘(Annh Pham)'18年

@AnhPham就是这样,这是执行默认功能的另一种方法
Amin Madani

切肉刀和简单的解决方案
Stephan Januar,

最富有表现力和最灵活的答案。
DawnSong
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.