it-roy-ru.com

Вызов реализации протокола по умолчанию из обычного метода

Мне интересно, возможно ли достичь такой цели.
У меня есть детская площадка, как это:

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 нужно все, что есть в реализации по умолчанию, плюс дополнительные вещи?
Это как-то похоже на вызов методов super. в classes для выполнения требования реализации каждого свойства и т.д., Но я не вижу возможности добиться того же с structs.

64
cojoj

Я не знаю, ищите ли вы ответ на этот вопрос, но способ сделать это - удалить функцию из определения протокола, привести ваш объект к 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"

По какой-то причине это работает, только если функция не объявлена ​​как часть протокола, а определена в расширении протокола. Пойди разберись. Но это работает.

73
Aaron Rasmussen

Ну, вы можете создать вложенный тип, соответствующий протоколу, создать его экземпляр и вызвать метод для него (не имеет значения, что вы не можете получить доступ к данным вашего типа, так как реализация внутри расширения протокола не может ссылаться на него в любом случае). Но это не решение, которое я бы назвал элегантным.

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

Если у вашего протокола есть требования associatedType или Self, приведение не будет работать. Чтобы обойти это, создайте «теневую» реализацию по умолчанию, которую могут вызывать как обычная реализация по умолчанию, так и соответствующий тип.

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()
    }
}
2
David James

Спасибо за пост! Если вы поместите определение функции в протокол, то когда объект будет приведен в качестве протокола, он увидит только версию объекта и, так как вы вызываете его внутри себя, вы получите новый адрес 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
2
Jim Malak

что вы думаете о таком способе исправить это?

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
Amin Madani