일반 메서드에서 프로토콜 기본 구현 호출


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 베타 모두에서 테스트 됨)
Antonio

1
즉, 😯 발표 한 이상한 구조입니다
cojoj

4
모든 인스턴스 메서드는 인스턴스를 첫 번째 매개 변수로 사용하는 정적 커리 메서드입니다
Antonio

그러나 확장을 제거하려고 시도했는데 동일한 세분화 오류가 발생합니다. 아마도 그것은 프로토콜과 함께 작동하지 않을 것입니다
Antonio

흠, 난이 쉽게 기본 구현을 사용하여 고정 할 수 있지만 코드에서 자신을 반복했습니다 것을 수치 ...
cojoj

답변:


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
오 맙소사! 나는 그들이 프로토콜에서 기능을 제거하도록 강요한다는 것을 믿을 수 없습니다! 좋은 대답, 감사합니다!
SkyWalker

5
이것은 나에게 버그처럼 보이지만 동일한 인스턴스를 캐스팅하여 다른 메서드 구현을 얻을 수는 없습니다.
MANIAK_dobrii

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
지금 당장 유일한 가능성 인 것 같습니다 (Apple에서 확인) ... 유용 할 수 있으므로이 기능에 대한 기능 레이더를 제출하겠습니다 👌
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

프로토콜에 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()
    }
}

더 이상 동의 할 수 없습니다. 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()

이것이 대답입니까?
Anh Pham

@AnhPham 기본 기능을 수행하기 위해, 또 다른 방법을 먹으 렴
아민마다 니에게

식칼 및 쉬운 솔루션
Stephan Januar 2019 년

가장 표현력 있고 유연한 답변입니다.
DawnSong
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.