Calling protocol default implementation from regular method

前端 未结 6 1626
暖寄归人
暖寄归人 2020-11-30 19:16

I\'m wondering if it\'s possible to achieve such a thing.
I have a Playground like this:

protocol Foo {
    func testPrint()
}

extension Foo {
    func          


        
相关标签:
6条回答
  • 2020-11-30 19:46

    Well, you could create a nested type conforming to the protocol, instantiate it, and call the method on that one (it does not matter that you cannot access your type's data as the implementation inside the protocol extension cannot reference it anyway). But it's not a solution I'd call elegant.

    struct Bar: Foo {
        func testPrint() {
            // Calling default implementation
            struct Dummy : Foo {}
            let dummy = Dummy()
            dummy.testPrint()
            print("Call from struct")
        }
    }
    
    0 讨论(0)
  • 2020-11-30 19:46

    In case your protocol has associatedType or Self requirements, then the cast will not work. To work around this, create a "shadow" default implementation that both the regular default implementation and the conforming type can call.

    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()
        }
    }
    
    0 讨论(0)
  • 2020-11-30 19:56

    I don't know if you are still looking for an answer to this, but the way to do it is to remove the function from the protocol definition, cast your object to Foo and then call the method on it:

    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"
    

    For some reason it only works if the function isn't declared as part of the protocol, but is defined in an extension to the protocol. Go figure. But it does work.

    0 讨论(0)
  • 2020-11-30 19:58

    Thanks for the post! If you put the function definition in the protocol then when the object is casted as the protocol it only sees the object's version of the function and since you are calling it inside itself you get the new address of Apple ...

    I did try a version like this:

    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()
    

    This gives an output of:

    Structure Version
    Extension Version
    
    0 讨论(0)
  • 2020-11-30 19:59

    I have come up with a solution for this.

    
    protocol Foo {
        var defaultImplementation: DefaultImpl? { get }
        func testPrint()
    }
    
    extension Foo {
        // Add default implementation
        var defaultImplementation: DefaultImpl? {
            get {
                return nil
            }
        }
    }
    
    struct DefaultImpl: Foo {
        func testPrint() {
            print("Foo")
        }
    }
    
    
    extension Foo {
        
        func testPrint() {
            defaultImplementation?.testPrint()
        }
    }
    
    struct Bar: Foo {
        
        var defaultImplementation: DefaultImpl? {
            get { return DefaultImpl() }
        }
        func testPrint() {
            defaultImplementation?.testPrint() // Prints "Foo"
        }
    }
    
    struct Baz: Foo {
        func testPrint() {
            print("Baz")
        }
    }
    
    
    let bar = Bar()
    bar.testPrint() // prints "Foo"
    
    let baz = Baz()
    baz.testPrint() // prints "Baz"
    
    
    
    0 讨论(0)
  • 2020-11-30 20:02

    what do you think about such way of fixing this ?

    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()
    
    0 讨论(0)
提交回复
热议问题