在Swift中将符合协议的类作为函数参数


91

在Objective-C中,可以将符合协议的类指定为方法参数。例如,我可以有一个只允许UIViewController符合的方法UITableViewDataSource

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

我找不到在Swift中执行此操作的方法(也许尚不可能)。您可以使用来指定多个协议func foo(obj: protocol<P1, P2>),但是您又如何要求该对象属于特定类呢?


您可以创建一个自定义类,例如MyViewControllerClass,并确保该类符合您关心的协议。然后声明参数接受该自定义类。我意识到这并不是在每种情况下都可行,但这是一种方法……虽然不能解决您的问题。更多解决方法。
CommaToast

Answers:


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<>提供更简洁的语法,例如Provides(但protocol<>不能包含非协议类型)。
jtbandes 2014年

这让我很难过。
DCMaxxx

只是出于好奇,您是否不能明确解开包装,numberOfSectionsInTableView因为它是其中的必需功能UITableViewDataSource
rb612

numberOfSectionsInTableView:是可选的-您可能会想到tableView:numberOfRowsInSection:
Nate Cook

11
在Swift 3中,此功能似乎func foo<T: UIViewController>(vc:T) where T:UITableViewDataSource { ... }
已从


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
链接到文档将很高兴看到。
2015年

4

使用Swift 3,您可以执行以下操作:

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

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

1
这仅适用于协议,不协议&类在迅速3
阿尔乔姆Goryaev

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
}

2

斯威夫特5:

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

所以本质上是Jeroen的回答。


0

2015年9月的注意事项:这是Swift早期的观察结果。

这似乎是不可能的。苹果在某些API中也有这种烦恼。这是iOS 8(自Beta 5起)中新引入的类的一个示例:

UIInputViewControllertextDocumentProxy属性:

在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不,你错了;如果像大多数协议一样声明UITextDocumentProxy,您仍然会拥有NSObject:@protocol MyAwesomeCallbacks <NSObject>
CommaToast,2016年

@CommaToast不在Swift中,这就是这个问题。
titaniumdecoy

@titaniumdecoy是的,您本来是对的。我很困惑!不好意思说你错了。从好的方面来说,在这种情况下,您仍然有NSObjectProtocol ...,但我知道这不是同一回事。
CommaToast
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.