Lớp tuân theo giao thức dưới dạng tham số hàm trong Swift


91

Trong Objective-C, có thể chỉ định một lớp tuân theo giao thức làm tham số phương thức. Ví dụ: tôi có thể có một phương thức chỉ cho phép một phương thức UIViewControllertuân theo UITableViewDataSource:

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

Tôi không thể tìm thấy cách nào để thực hiện việc này trong Swift (có lẽ vẫn chưa thể thực hiện được). Bạn có thể chỉ định nhiều giao thức bằng cách sử dụng func foo(obj: protocol<P1, P2>), nhưng làm thế nào để bạn yêu cầu đối tượng đó cũng thuộc một lớp cụ thể?


Bạn có thể tạo một lớp tùy chỉnh, ví dụ như MyViewControllerClass và đảm bảo rằng lớp đó tuân thủ giao thức mà bạn quan tâm. Sau đó khai báo đối số chấp nhận lớp tùy chỉnh đó. Tôi nhận ra rằng nó sẽ không hoạt động cho mọi tình huống nhưng, đó là một cách ... không phải là một câu trả lời cho câu hỏi của bạn. Nhiều giải pháp khác.
CommaToast

Câu trả lời:


132

Bạn có thể định nghĩa foonhư một hàm chung và sử dụng các ràng buộc kiểu để yêu cầu cả một lớp và một giao thức.

Swift 4

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

Swift 3 (cũng hoạt động cho 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
Tôi nghĩ rằng hơi đáng tiếc khi điều này là bắt buộc. Hy vọng rằng trong tương lai sẽ có một cú pháp rõ ràng hơn cho việc này, như protocol<>cung cấp (nhưng protocol<>không thể chứa các loại không phải giao thức).
jtbandes

Điều này làm tôi rất buồn.
DCMaxxx

Chỉ vì tò mò, bạn không thể mở ra một cách rõ ràng numberOfSectionsInTableViewvì nó là một chức năng bắt buộc trong UITableViewDataSource?
rb612

numberOfSectionsInTableView:là tùy chọn — bạn có thể đang nghĩ đến tableView:numberOfRowsInSection:.
Nate Cook,

11
Trong Swift 3 này dường như bị phản đối như của Xcode 8 beta 6 với một sở thích cho:func foo<T: UIViewController>(vc:T) where T:UITableViewDataSource { ... }
LOP_Luke

29

Trong Swift 4, bạn có thể đạt được điều này với dấu & mới:

let vc: UIViewController & UITableViewDataSource

17

Tài liệu sách Swift gợi ý rằng bạn sử dụng các ràng buộc kiểu với mệnh đề where:

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

Điều này đảm bảo rằng "inParam" thuộc loại "SomeClass" với một điều kiện là nó cũng tuân theo "SomeProtocol". Bạn thậm chí có quyền chỉ định nhiều mệnh đề được phân tách bằng dấu phẩy:

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

1
Liên kết đến tài liệu sẽ rất hay khi xem.
Raj

4

Với Swift 3, bạn có thể làm như sau:

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

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

1
Điều này chỉ áp dụng cho các giao thức, chứ không phải giao thức & lớp trong nhanh chóng 3.
Artem Goryaev

2

Còn cách này thì sao ?:

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

Swift 5:

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

Vì vậy, về cơ bản câu trả lời của Jeroen ở trên.


0

Ghi chú vào tháng 9 năm 2015 : Đây là một quan sát trong những ngày đầu của Swift.

Nó dường như là không thể. Apple cũng gặp khó khăn này trong một số API của họ. Dưới đây là một ví dụ từ một lớp mới được giới thiệu trong iOS 8 (kể từ phiên bản beta 5):

UIInputViewController's textDocumentProxytài sản:

Được định nghĩa trong Objective-C như sau:

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

và trong Swift:

var textDocumentProxy: NSObject! { get }

Liên kết đến tài liệu của Apple: https://developer.apple.com/library/prerelease/iOS/documentation/UIKit/Reference/UIInputViewController_Class/index.html#//apple_ref/occ/instp/UIInputViewController/textDocumentProxy


1
Điều này dường như được tạo tự động: Các giao thức Swift có thể được chuyển xung quanh dưới dạng các đối tượng. Về mặt lý thuyết, họ có thể chỉ cần gõvar textDocumentProxy: UITextDocumentProxy! { get }
atlex2

@ atlex2 Bạn đã mất loại lớp NSObject thay cho loại giao thức UITextDocumentProxy.
titaniumdecoy

@titaniumdecoy Không, bạn nhầm rồi; bạn vẫn có NSObject nếu UITextDocumentProxy được khai báo giống như hầu hết các giao thức bao gồm:@protocol MyAwesomeCallbacks <NSObject>
CommaToast

@CommaToast Không có trong Swift, đó là nội dung câu hỏi này.
titaniumdecoy

@titaniumdecoy Vâng, ban đầu bạn đã đúng. Tôi đã nhầm lẫn! Xin lỗi khi nói rằng bạn đã sai. Về mặt tích cực, bạn vẫn có NSObjectProtocol ... trong trường hợp này ... nhưng tôi biết nó không giống nhau.
CommaToast
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.