developer tip

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

optionbox 2020. 9. 6. 09:30
반응형

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


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변수 선언을 준수하도록 지정할 수 있기를 원합니까?

정당화

팩토리 패턴에 익숙하다면 특정 인터페이스를 구현하는 특정 클래스의 객체를 반환 할 수 있다는 맥락에서 의미가있을 것입니다.

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

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


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

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는 여러 프로토콜로 클래스를 구성하는 개념을 도입했습니다.


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

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

이를 통해 기술적 cell으로 asProtocol.

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

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

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

Unfortunately, Swift does not support object level protocol conformance. However, there is a somewhat awkward work-around that may serve your purposes.

struct VCWithSomeProtocol {
    let protocol: SomeProtocol
    let viewController: UIViewController

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

Then, anywhere you need to do anything that UIViewController has, you would access the .viewController aspect of the struct and anything you need the protocol aspect, you would reference the .protocol.

For Instance:

class SomeClass {
   let mySpecialViewController: VCWithSomeProtocol

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

Now anytime you need mySpecialViewController to do anything UIViewController related, you just reference mySpecialViewController.viewController and whenever you need it to do some protocol function, you reference mySpecialViewController.protocol.

Hopefully Swift 4 will allow us to declare an object with protocols attached to it in the future. But for now, this works.

Hope this helps!


EDIT: I was mistaken, but if somebody else read this misunderstanding like me, I leave this answer out there. The OP asked about checking for protocol conformance of the object of a given subclass, and that is another story as the accepted answer shows. This answer talks about protocol conformance for the base class.

Maybe I'm mistaken, but are you not talking about adding protocol conformance to the UITableCellView class? The protocol is in that case extended to the base class, and not the object. See Apple's documentation on Declaring Protocol Adoption with an Extension which in your case would be something like:

extension UITableCellView : ProtocolOne {}

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

In addition to the already referenced Swift documentation, also see Nate Cooks article Generic functions for incompatible types with further examples.

This gives us the flexibility of being able to deal with the implementation of the base type as well as the added interface defined in the protocol.

Is there another more obvious way that I might be missing?

Protocol Adoption will do just this, make an object adhere to the given protocol. Be however aware of the adverse side, that a variable of a given protocol type does not know anything outside of the protocol. But this can be circumvented by defining a protocol which has all the needed methods/variables/...

Whilst the supplied type does not exactly conform to the mentioned interface, the object the factory returns does and so I would like the flexibility in interacting with both the base class type and the declared protocol interface

If you would like for a generic method, variable to conform to both a protocol and base class types, you could be out of luck. But it sounds like you need to define the protocol wide enough to have the needed conformance methods, and at the same time narrow enough to have the option to adopt it to base classes without too much work (i.e. just declaring that a class conforms to the protocol).


I once had a similar situation when trying to link my generic interactor connections in Storyboards (IB won't allow you to connect outlets to protocols, only object instances), which I got around by simply masking the base class public ivar with a private computed property. While this does not prevent someone from making illegal assignments per se, it does provide a convenient way to safely prevent any unwanted interaction with a non-conforming instance at runtime. (i.e. prevent calling delegate methods to objects that don't conform to the protocol.)

Example:

@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)
    }
}

The "outputReceiver" is declared optional, as is the private "protocolOutputReceiver". By always accessing the outputReceiver (a.k.a. delegate) through the latter (the computed property), I effectively filter out any objects that do not conform to the protocol. Now I can simply use optional chaining to safely call out to the delegate object whether or not it implements the protocol or even exists.

To apply this to your situation, you can have the public ivar be of type "YourBaseClass?" (as opposed to AnyObject), and use the private computed property to enforce the protocol conformance. FWIW.

참고URL : https://stackoverflow.com/questions/26401778/in-swift-how-can-i-declare-a-variable-of-a-specific-type-that-conforms-to-one-o

반응형