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


91

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

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

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


Вы можете создать собственный класс, например MyViewControllerClass, и убедиться, что этот класс соответствует протоколу, который вам нужен. Затем объявите, что аргумент принимает этот настраиваемый класс. Я понимаю, что это не сработает для каждой ситуации, но это способ ... не ответ на ваш вопрос. Скорее обходной путь.
CommaToast

Ответы:


133

Вы можете определить 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)
}

3
Я считаю, что это немного прискорбно, что это требуется. Надеюсь, что в будущем для этого будет более чистый синтаксис, например protocol<>предоставляет (но protocol<>не может содержать типы, не относящиеся к протоколу).
jtbandes

Это меня оооочень грустно.
DCMaxxx,

Просто из любопытства, не можете ли вы явно развернуть, numberOfSectionsInTableViewпотому что это обязательная функция UITableViewDataSource?
rb612

numberOfSectionsInTableView:необязательно - возможно, вы думаете tableView:numberOfRowsInSection:.
Nate Cook

11
В Swift 3 это, по-видимому, устарело с Xcode 8 beta 6 с предпочтением:func foo<T: UIViewController>(vc:T) where T:UITableViewDataSource { ... }
LOP_Luke


17

Документация по книге Swift предлагает вам использовать ограничения типа с предложением 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 }

1
Ссылку на документацию было бы неплохо увидеть.
Радж

4

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

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

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

1
Это относится только к протоколам, а не к протоколу и классу в Swift 3.
Артем Горяев

3

Swift 5:

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

Таким образом , по существу , Jeroen ответ «S выше.


2

А как насчет этого ?:

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
}

0

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

Это кажется невозможным. У 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


1
Кажется, что это сгенерировано автоматически: протоколы Swift можно передавать как объекты. Теоретически они могли просто набратьvar textDocumentProxy: UITextDocumentProxy! { get }
atlex2

@ atlex2 Вы потеряли тип класса NSObject в пользу типа протокола UITextDocumentProxy.
titaniumdecoy

@titaniumdecoy Нет, ты ошибаешься; у вас все еще есть NSObject, если UITextDocumentProxy объявлен, как и большинство протоколов:@protocol MyAwesomeCallbacks <NSObject>
CommaToast

@CommaToast Не в Swift, вот о чем этот вопрос.
titaniumdecoy

@titaniumdecoy Да, изначально ты был прав. Я был сбит с толку! Извините, что вы ошибались. С другой стороны, у вас все еще есть NSObjectProtocol ... в этом случае ... но я знаю, что это не то же самое.
CommaToast
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.