Класс, соответствующий протоколу как параметр функции в Swift


в Objective-C в качестве параметра метода можно указать класс, соответствующий протоколу. Например, у меня может быть метод, который позволяет только UIViewController, что соответствует UITableViewDataSource:

- (void)foo:(UIViewController<UITableViewDataSource> *)vc;

Я не могу найти способ сделать это в Swift (возможно, это не возможно, пока). Вы можете указать несколько протоколов с помощью func foo(obj: protocol<P1, P2>), но как вы требуете, чтобы объект также принадлежал к определенному классу?

6 71

6 ответов:

вы можете определить foo как универсальная функция и использовать ограничения типа, чтобы требовать как класс, так и протокол.

Swift 4

func foo<T: UIViewController & UITableViewDataSource>(vc: T) {
    .....
}

Swift 3 (работает также для Swift 4)

func foo<T: UIViewController>(vc:T) where T:UITableViewDataSource { 
    ....
}

Swift 2

func foo<T: UIViewController where T: UITableViewDataSource>(vc: T) {
    // access UIViewController property
    let view = vc.view
    // call UITableViewDataSource method
    let sections = vc.numberOfSectionsInTableView?(tableView)
}

в документации Swift book предлагается использовать ограничения типа с предложением where:

func someFunction<C1: SomeClass where C1:SomeProtocol>(inParam: C1) {}

это гарантирует, что" inParam "имеет тип" SomeClass "с условием, что он также придерживается"SomeProtocol". У вас даже есть возможность указать несколько предложений where, разделенных запятой:

func itemsMatch<C1: SomeProtocol, C2: SomeProtocol where C1.ItemType == C2.ItemType,    C1.ItemType: SomeOtherProtocol>(foo: C1, bar: C2) -> Bool { return true }

в Swift 4 Вы можете достичь этого с помощью нового знака&:

let vc: UIViewController & UITableViewDataSource

С Swift 3, Вы можете сделать следующее:

func foo(_ dataSource: UITableViewDataSource) {
    self.tableView.dataSource = dataSource
}

func foo(_ delegateAndDataSource: UITableViewDelegate & UITableViewDataSource) { 
    //Whatever
}

Примечание в сентябре 2015 года: это было наблюдение в первые дни Свифт.

Это, кажется, невозможно. Apple также имеет это раздражение в некоторых своих API. Вот один пример из недавно введенного класса в iOS 8 (начиная с бета-версии 5):

UIInputViewController ' s textDocumentProxy свойства:

определены в Objective-C следующим образом:

@property(nonatomic, readonly) NSObject<UITextDocumentProxy> *textDocumentProxy;

и в Swift:

var textDocumentProxy: NSObject! { get }

ссылка на Apple' документация: https://developer.apple.com/library/prerelease/iOS/documentation/UIKit/Reference/UIInputViewController_Class/index.html#//apple_ref/occ/instp/UIInputViewController/textDocumentProxy

Как насчет этого пути?:

protocol MyProtocol {
    func getTableViewDataSource() -> UITableViewDataSource
    func getViewController() -> UIViewController
}

class MyVC : UIViewController, UITableViewDataSource, MyProtocol {

    // ...

    func getTableViewDataSource() -> UITableViewDataSource {
        return self
    }

    func getViewController() -> UIViewController {
        return self
    }
}

func foo(_ vc:MyProtocol) {
    vc.getTableViewDataSource() // working with UITableViewDataSource stuff
    vc.getViewController() // working with UIViewController stuff
}