[generics] Swift : 제네릭 유형이 프로토콜을 준수하는지 확인

다음과 같이 정의한 프로토콜이 있습니다.

protocol MyProtocol {
   ...
}

또한 일반 구조체가 있습니다.

struct MyStruct <T>  {
    ...
}

마지막으로 일반적인 기능이 있습니다.

func myFunc <T> (s: MyStruct<T>) -> T? {
   ...
}

T 유형이 MyProtocol을 준수하는지 함수 내부를 테스트하고 싶습니다. 본질적으로 나는 할 수 있기를 원합니다 (~ 의사 코드) :

let conforms = T.self is MyProtocol

그러나 이것은 컴파일러 오류를 발생시킵니다.

error: cannot downcast from 'T.Type' to non-@objc protocol type 'MyProtocol'
   let conforms = T.self is MyProtocol
                  ~~~~~~ ^  ~~~~~~~~~~

나는 또한 변화 같은 시도 T.self is MyProtocol.self, T is MyProtocol그리고 사용하는 ==대신을 is. 지금까지 나는 아무데도 가지 않았다. 어떤 아이디어?



답변

@Alex가 T유형이 아닌 프로토콜을 준수 하는지 확인하고 싶습니다 s. 그리고 일부 응답자는 명확하게 보지 못했습니다.

확인 T유형은 다음과 같은 프로토콜을 따릅니다.

if let _ = T.self as? MyProtocol.Type {
    //  T conform MyProtocol
}

또는

if T.self is MyProtocol.Type {
    //  T conform MyProtocol
}


답변

조금 늦었지만 테스트로 프로토콜에 응답하는지 테스트 할 수 있습니다 as ?.

if let currentVC = myViewController as? MyCustomProtocol {
    // currentVC responds to the MyCustomProtocol protocol =]
}

편집 : 조금 더 짧게 :

if let _ = self as? MyProtocol {
    // match
}

그리고 가드 사용 :

guard let _ = self as? MyProtocol else {
    // doesn't match
    return
}


답변

가장 간단한 대답은 : 그렇게하지 마십시오. 대신 오버로딩 및 제약 조건을 사용하고 런타임에 동적으로 테스트하는 대신 컴파일 타임에 모든 것을 미리 결정하십시오. 런타임 유형 검사 및 컴파일 타임 제네릭은 스테이크와 아이스크림과 비슷합니다. 둘 다 좋지만 혼합하는 것은 약간 이상합니다.

다음과 같은 것을 고려하십시오.

protocol MyProtocol { }

struct MyStruct <T>  { let val: T }

func myFunc<T: MyProtocol>(s: MyStruct<T>) -> T? {
    return s.val
}

func myFunc<T>(s: MyStruct<T>) -> T? {
    return nil
}

struct S1: MyProtocol { }
struct S2 { }

let m1 = MyStruct(val: S1())
let m2 = MyStruct(val: S2())

myFunc(m1) // returns an instance of S1
myFunc(m2) // returns nil, because S2 doesn't implement MyProtocol

단점은 T가 런타임에 프로토콜을 지원하면 동적으로 설정할 수 없다는 것입니다.

let o: Any = S1()
let m3 = MyStruct(val: o)
myFunc(m3)  // will return nil even though o 
            // does actually implement MyProtocol

그러나 솔직히 말해서 일반 함수 내에서 그렇게해야합니까? 실제 유형이 무엇인지 확실하지 않은 경우 더 나은 옵션은 나중에 지연하고 알아 내기 위해 제네릭 함수 내에서 찌르는 것보다 미리 파악하는 것입니다.


답변

여러 유형의 케이스를 처리하려는 경우 swift의 스위치 케이스 패턴 일치를 활용할 수도 있습니다 T.

func myFunc<T>(s: MyStruct<T>) -> T? {
    switch s {
    case let sType as MyProtocol:
        // do MyProtocol specific stuff here, using sType
    default:
        //this does not conform to MyProtocol
    ...
    }
}


답변

준수하자 = T.self는 MyProtocol.Type입니다.


답변

프로토콜을 @objc다음 과 같이 선언해야합니다 .

@objc protocol MyProtocol {
    ...
}

Apple의 “The Swift Programming Language”책에서 발췌 :

위의 HasArea 프로토콜에서 볼 수 있듯이 프로토콜이 @objc 속성으로 표시된 경우에만 프로토콜 적합성을 확인할 수 있습니다. 이 속성은 프로토콜이 Objective-C 코드에 노출되어야 함을 나타내며 Cocoa 및 Objective-C와 함께 Swift 사용에 설명되어 있습니다. Objective-C와 상호 운용되지 않더라도 프로토콜 적합성을 확인하려면 @objc 속성으로 프로토콜을 표시해야합니다.

또한 @objc 프로토콜은 구조 나 열거가 아닌 클래스에 의해서만 채택 될 수 있습니다. 적합성을 확인하기 위해 프로토콜을 @objc로 표시하면 해당 프로토콜을 클래스 유형에만 적용 할 수 있습니다.


답변

테스트 케이스의 경우 다음과 같이 적합성을 확인합니다.

let conforms: Bool = (Controller.self as Any) is Protocol.Type