Swift에서 하나 이상의 프로토콜을 준수하는 특정 유형의 변수를 어떻게 선언 할 수 있습니까?


96

Swift에서는 다음과 같이 선언하여 변수의 유형을 명시 적으로 설정할 수 있습니다.

var object: TYPE_NAME

한 단계 더 나아가 여러 프로토콜을 준수하는 변수를 protocol선언 하려면 선언적을 사용할 수 있습니다 .

var object: protocol<ProtocolOne,ProtocolTwo>//etc

하나 이상의 프로토콜을 따르고 특정 기본 클래스 유형 인 객체를 선언하려면 어떻게해야합니까? Objective-C는 다음과 같습니다.

NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;

Swift에서는 다음과 같이 보일 것으로 예상합니다.

var object: TYPE_NAME,ProtocolOne//etc

이는 프로토콜에 정의 된 추가 인터페이스뿐만 아니라 기본 유형의 구현을 처리 할 수있는 유연성을 제공합니다.

내가 놓칠 수있는 또 다른 분명한 방법이 있습니까?

예를 들어 UITableViewCell프로토콜에 따라 셀을 반환 하는 공장이 있다고 가정 해 보겠습니다 . 프로토콜을 따르는 셀을 반환하는 일반 함수를 쉽게 설정할 수 있습니다.

class CellFactory {
    class func createCellForItem<T: UITableViewCell where T:MyProtocol >(item: SpecialItem,tableView: UITableView) -> T {
        //etc
    }
}

나중에 유형과 프로토콜을 모두 활용하면서 이러한 셀을 대기열에서 빼고 싶습니다.

var cell: MyProtocol = CellFactory.createCellForItem(somethingAtIndexPath) as UITableViewCell

테이블 뷰 셀이 프로토콜을 따르지 않기 때문에 오류가 반환됩니다.

셀이 a UITableViewCell이고 MyProtocol변수 선언을 준수하도록 지정할 수 있기를 원합니까?

정당화

Factory Pattern에 익숙하다면 특정 인터페이스를 구현하는 특정 클래스의 객체를 반환 할 수 있다는 맥락에서 의미가 있습니다.

제 예에서와 같이 때때로 특정 객체에 적용 할 때 의미가있는 인터페이스를 정의하고 싶습니다. 테이블 뷰 셀의 내 예는 그러한 정당성 중 하나입니다.

제공된 유형이 언급 된 인터페이스와 정확히 일치하지는 않지만 팩토리가 반환하는 객체는 수행하므로 기본 클래스 유형 및 선언 된 프로토콜 인터페이스와 상호 작용하는 유연성을 원합니다.


미안하지만, 이것의 요점은 신속합니다. 유형은 이미 준수하는 프로토콜을 알고 있습니다. 유형을 사용하지 않는 것은 무엇입니까?
Kirsteins 2014 년

1
@Kirsteins이 유형은 공장에서 반환 따라서 공통 기본 클래스와 제네릭 형식입니다하지 않는 한
다니엘 Galasko

가능하면 예를 들어주세요.
Kirsteins 2014 년

NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;. 이 객체는 NSSomething그것이 무엇을 따르는 지 이미 알고 있기 때문에 매우 쓸모없는 것 같습니다 . 그것의 프로토콜 중 하나를 준수하지 않을 경우 <>얻을 것이다 당신 unrecognised selector ...충돌. 이것은 유형 안전을 전혀 제공하지 않습니다.
Kirsteins 2014 년

@Kirsteins, 다시는 내 예제를 참조하십시오의 당신이 당신의 공장 밖으로 vends 객체가 지정된 프로토콜에 부합하는 특정 기본 클래스의 것을 알고 때 사용
다니엘 Galasko

답변:


72

Swift 4에서는 이제 유형의 하위 클래스 인 변수를 선언하고 동시에 하나 이상의 프로토콜을 구현할 수 있습니다.

var myVariable: MyClass & MyProtocol & MySecondProtocol

선택적 변수를 수행하려면 :

var myVariable: (MyClass & MyProtocol & MySecondProtocol)?

또는 메소드의 매개 변수로 :

func shakeEm(controls: [UIControl & Shakeable]) {}

Apple은 WWDC 2017 세션 402 : Whats new in Swift 에서이를 발표했습니다 .

둘째, 클래스와 프로토콜 구성에 대해 이야기하고 싶습니다. 그래서 여기에서는 약간의 흔들림 효과를 줄 수있는 UI 요소에 대한이 흔들림 가능한 프로토콜을 도입하여 그 자체로 관심을 끌었습니다. 그리고 실제로이 흔들기 기능을 제공하기 위해 UIKit 클래스 중 일부를 확장했습니다. 이제 저는 간단 해 보이는 것을 작성하고 싶습니다. 흔들릴 수있는 여러 컨트롤을 사용하고 관심을 끌 수있는 컨트롤을 흔들어주는 함수를 작성하고 싶습니다. 이 배열에 어떤 유형을 쓸 수 있습니까? 실제로 실망스럽고 까다 롭습니다. 그래서 UI 컨트롤을 사용해 볼 수 있습니다. 하지만이 게임에서 모든 UI 컨트롤이 흔들리는 것은 아닙니다. shakable을 시도해 볼 수 있지만 모든 shakable이 UI 컨트롤은 아닙니다. 그리고 실제로 Swift 3에서 이것을 표현하는 좋은 방법은 없습니다.Swift 4는 여러 프로토콜로 클래스를 구성하는 개념을 도입했습니다.


3
다만 신속한 진화의 제안에 링크를 추가 github.com/apple/swift-evolution/blob/master/proposals/...
다니엘 Galasko

Philipp 감사합니다!
Omar Albeik

이 유형의 선택적 변수가 필요한 경우 어떻게합니까?
Vyachaslav Gerchicov

2
@VyachaslavGerchicov : 괄호를 둘러싼 다음 물음표를 다음과 같이 넣을 수 있습니다. var myVariable : (MyClass & MyProtocol & MySecondProtocol)?
Philipp Otto

30

다음과 같은 변수를 선언 할 수 없습니다.

var object:Base,protocol<ProtocolOne,ProtocolTwo> = ...

함수 반환 유형을 다음과 같이 선언하지도 않습니다.

func someFunc() -> Base,protocol<MyProtocol,Protocol2> { ... }

이와 같은 함수 매개 변수로 선언 할 수 있지만 기본적으로 상향 캐스팅입니다.

func someFunc<T:Base where T:protocol<MyProtocol1,MyProtocol2>>(val:T) {
    // here, `val` is guaranteed to be `Base` and conforms `MyProtocol` and `MyProtocol2`
}

class SubClass:BaseClass, MyProtocol1, MyProtocol2 {
   //...
}

let val = SubClass()
someFunc(val)

지금은 다음과 같이 할 수 있습니다.

class CellFactory {
    class func createCellForItem(item: SpecialItem) -> UITableViewCell {
        return ... // any UITableViewCell subclass
    }
}

let cell = CellFactory.createCellForItem(special)
if let asProtocol = cell as? protocol<MyProtocol1,MyProtocol2> {
    asProtocol.protocolMethod()
    cell.cellMethod()
}

이것으로 기술적으로 cellasProtocol .

단, 컴파일러 cell는 인터페이스 UITableViewCell만 있고 asProtocol프로토콜 인터페이스 만 있습니다. 따라서 UITableViewCell의 메서드 를 호출 하려면 cell변수 를 사용해야 합니다. 프로토콜 메서드를 호출하려면asProtocol 변수를 하십시오.

셀이 프로토콜을 준수한다고 확신하는 경우을 사용할 필요가 없습니다 if let ... as? ... {}. 처럼:

let cell = CellFactory.createCellForItem(special)
let asProtocol = cell as protocol<MyProtocol1,MyProtocol2>

공장에서 반환 유형을 지정하므로 선택적 캐스트를 수행하는 데 기술적으로 필요하지 않습니까? 명시 적으로 프로토콜을 선언하는 타이핑을 수행하기 위해 swifts 암시 적 타이핑에 의존 할 수 있습니까?
Daniel Galasko 2014 년

무슨 말인지 이해가 안 돼요. 저의 영어 실력이 나빠서 죄송 해요. 에 대해 말하는 경우은 제네릭 유형이 아니기 -> UITableViewCell<MyProtocol>때문에 유효하지 UITableViewCell않습니다. 나는 이것이 컴파일조차하지 않는다고 생각한다.
rintaro

나는 당신의 일반적인 구현을 언급하는 것이 아니라 구현의 예를 언급합니다. let asProtocol = ...
Daniel Galasko 2014 년

있는 UITableViewCell로 프로토콜 <ProtocolOne, ProtocolTwo> = 여기서 someObject를 하나 개의 변수에 모두의 이익을 얻을 : VAR 셀 : 나, 난 그냥 할 수
다니엘 Galasko

2
나는 그렇게 생각하지 않는다. 당신이 그렇게 할 수 있다고하더라도, cell(컴파일러 용) 프로토콜 메서드 만 있습니다.
rintaro

2

불행히도 Swift는 객체 수준 프로토콜 준수를 지원하지 않습니다. 그러나 귀하의 목적에 부합 할 수있는 다소 어색한 해결 방법이 있습니다.

struct VCWithSomeProtocol {
    let protocol: SomeProtocol
    let viewController: UIViewController

    init<T: UIViewController>(vc: T) where T: SomeProtocol {
        self.protocol = vc
        self.viewController = vc
    }
}

그런 다음 UIViewController에있는 모든 작업을 수행해야하는 곳이면 어디에서나 구조체의 .viewController 측면과 프로토콜 측면에 필요한 모든 것에 액세스하고 .protocol을 참조합니다.

예를 들어 :

class SomeClass {
   let mySpecialViewController: VCWithSomeProtocol

   init<T: UIViewController>(injectedViewController: T) where T: SomeProtocol {
       self.mySpecialViewController = VCWithSomeProtocol(vc: injectedViewController)
   }
}

이제 UIViewController와 관련된 작업을 수행하기 위해 mySpecialViewController가 필요할 때마다 mySpecialViewController.viewController를 참조하고 프로토콜 기능을 수행하기 위해 필요할 때마다 mySpecialViewController.protocol을 참조합니다.

바라건대 Swift 4를 사용하면 앞으로 프로토콜이 첨부 된 객체를 선언 할 수 있습니다. 그러나 지금은 작동합니다.

도움이 되었기를 바랍니다!


1

편집 : 나는 착각 했지만 다른 사람이 나처럼 오해를 읽은 경우이 답변을 남겨 둡니다. OP는 주어진 하위 클래스의 객체에 대한 프로토콜 적합성을 확인하는 것에 대해 물었고, 수용된 답변이 보여주는 또 다른 이야기입니다. 이 답변은 기본 클래스에 대한 프로토콜 준수에 대해 설명합니다.

내가 틀렸을지도 모르지만 UITableCellView클래스에 프로토콜 준수를 추가하는 것에 대해 이야기하고 있지 않습니까? 이 경우 프로토콜은 개체가 아닌 기본 클래스로 확장됩니다. 확장 이있는 프로토콜 채택 선언 에 대한 Apple의 문서를 참조하십시오 .

extension UITableCellView : ProtocolOne {}

// Or alternatively if you need to add a method, protocolMethod()
extension UITableCellView : ProcotolTwo {
   func protocolTwoMethod() -> String {
     return "Compliant method"
   }
}

이미 참조 된 Swift 문서에 더하여 Nate Cooks 기사 Generic functions for incompatible types with additional examples.

이는 프로토콜에 정의 된 추가 인터페이스뿐만 아니라 기본 유형의 구현을 처리 할 수있는 유연성을 제공합니다.

내가 놓칠 수있는 또 다른 분명한 방법이 있습니까?

프로토콜 채택은이 작업을 수행하고 객체가 주어진 프로토콜을 준수하도록합니다. 지정된 프로토콜 유형의 변수가하는, 불리한 측면하지만주의 하지 외부에서 아무것도 알지 . 그러나 이것은 필요한 모든 방법 / 변수 / ...를 포함하는 프로토콜을 정의함으로써 피할 수 있습니다.

제공된 유형이 언급 된 인터페이스와 정확히 일치하지는 않지만 팩토리가 반환하는 객체는 수행하므로 기본 클래스 유형 및 선언 된 프로토콜 인터페이스와 상호 작용하는 유연성을 원합니다.

프로토콜 및 기본 클래스 유형을 모두 준수하는 일반 메서드, 변수를 원한다면 운이 좋지 않을 수 있습니다. 그러나 필요한 적합성 메서드를 갖출 수있을만큼 프로토콜을 넓게 정의해야하는 것처럼 들리며 동시에 너무 많은 작업없이 기본 클래스에 채택 할 수있는 옵션을 가질 수있을만큼 충분히 좁아 야합니다 (즉, 클래스가 실험 계획안).


1
그게 제가 말하는 것이 아니라 감사합니다 :) 클래스와 특정 프로토콜을 통해 객체와 인터페이스 할 수 있기를 원했습니다. obj-c에서 어떻게 할 수 있는지 NSObject <MyProtocol> obj = ... 말할 필요도없이이 작업은 신속하게 수행 할 수 없습니다. 객체를 프로토콜로 캐스팅해야합니다
Daniel Galasko

0

스토리 보드에서 일반 인터랙 터 연결을 연결하려고 할 때 비슷한 상황이 발생했습니다. 특성. 이것은 누군가가 그 자체로 불법적 인 할당을하는 것을 막지는 못하지만, 런타임에 부적합한 인스턴스와 원치 않는 상호 작용을 안전하게 방지하는 편리한 방법을 제공합니다. (즉, 프로토콜을 따르지 않는 객체에 대한 델리게이트 메서드 호출을 방지합니다.)

예:

@objc protocol SomeInteractorInputProtocol {
    func getSomeString()
}

@objc protocol SomeInteractorOutputProtocol {
    optional func receiveSomeString(value:String)
}

@objc class SomeInteractor: NSObject, SomeInteractorInputProtocol {

    @IBOutlet var outputReceiver : AnyObject? = nil

    private var protocolOutputReceiver : SomeInteractorOutputProtocol? {
        get { return self.outputReceiver as? SomeInteractorOutputProtocol }
    }

    func getSomeString() {
        let aString = "This is some string."
        self.protocolOutputReceiver?.receiveSomeString?(aString)
    }
}

"outputReceiver"는 개인용 "protocolOutputReceiver"와 마찬가지로 선택 사항으로 선언됩니다. 후자 (계산 된 속성)를 통해 항상 outputReceiver (대리자)에 액세스함으로써 프로토콜을 준수하지 않는 모든 개체를 효과적으로 필터링합니다. 이제 단순히 선택적 체인을 사용하여 프로토콜을 구현하는지 여부에 관계없이 위임 개체를 안전하게 호출 할 수 있습니다.

이를 상황에 적용하려면 공용 ivar를 "YourBaseClass?"유형으로 만들 수 있습니다. (AnyObject와 반대), 프로토콜 준수를 적용하기 위해 개인 계산 속성을 사용합니다. FWIW.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.