Клас, що відповідає протоколу як функціональний параметр у Swift


91

У Objective-C як параметр методу можна вказати клас, що відповідає протоколу. Наприклад, я міг би мати метод, який дозволяє лише a, UIViewControllerякий відповідає UITableViewDataSource:

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

Я не можу знайти спосіб зробити це у Свіфті (можливо, це поки не можливо). Ви можете вказати кілька протоколів, використовуючи func foo(obj: protocol<P1, P2>), але як вам потрібно, щоб об'єкт також був певного класу?


Ви можете створити власний клас, наприклад MyViewControllerClass, і переконатися, що клас відповідає вашому важливому протоколу. Потім оголосіть, що аргумент приймає цей власний клас. Я розумію, що це не спрацювало б у кожній ситуації, але це спосіб ... не відповідь на ваше запитання. Більше обхідного шляху.
CommaToast

Відповіді:


132

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

Стрімкий 4

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

Swift 3 (працює і для Swift 4)

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

Стрімкий 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". У вас навіть є можливість вказати кілька речень, де розділено комою:

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
Це стосується лише протоколів, а не протоколу та класу в швидкому темпі 3.
Артем Горяєв

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 року : Це було спостереження в перші дні Свіфта.

Здається, це неможливо. Apple має це роздратування і в деяких їх API. Ось один приклад із нещодавно представленого класу в iOS 8 (станом на бета-версію 5):

UIInputViewController«S textDocumentProxyвласності:

Визначено в Objective-C наступним чином:

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

і в Свіфт:

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.