Kelas yang sesuai dengan protokol sebagai parameter fungsi di Swift


91

Di Objective-C, dimungkinkan untuk menentukan kelas yang sesuai dengan protokol sebagai parameter metode. Misalnya, saya dapat memiliki metode yang hanya mengizinkan a UIViewControlleryang sesuai dengan UITableViewDataSource:

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

Saya tidak dapat menemukan cara untuk melakukan ini di Swift (mungkin belum memungkinkan). Anda dapat menentukan beberapa protokol menggunakan func foo(obj: protocol<P1, P2>), tetapi bagaimana Anda meminta objek dari kelas tertentu juga?


Anda dapat membuat kelas khusus, misalnya MyViewControllerClass, dan memastikan bahwa kelas tersebut sesuai dengan protokol yang Anda pedulikan. Kemudian deklarasikan argumen menerima kelas kustom itu. Saya menyadari itu tidak akan berhasil untuk setiap situasi tetapi, ini adalah cara ... bukan jawaban atas pertanyaan Anda. Lebih dari sebuah solusi.
CommaToast

Jawaban:


133

Anda dapat mendefinisikan foosebagai fungsi generik dan menggunakan batasan tipe untuk membutuhkan kelas dan protokol.

Cepat 4

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

Swift 3 (juga berfungsi untuk Swift 4)

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

Cepat 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
Saya pikir agak disayangkan hal ini diperlukan. Semoga kedepannya akan ada sintaks yang lebih bersih untuk ini, seperti protocol<>sediakan (tapi protocol<>tidak bisa berisi tipe non-protokol).
jtbandes

Ini membuatku sangat sedih.
DCMaxxx

Hanya ingin tahu, tidak bisakah Anda membuka bungkusnya secara eksplisit numberOfSectionsInTableViewkarena ini adalah fungsi yang diwajibkan UITableViewDataSource?
rb612

numberOfSectionsInTableView:adalah opsional — Anda mungkin memikirkan tableView:numberOfRowsInSection:.
Nate Cook

11
Di Swift 3 ini tampaknya tidak digunakan lagi pada Xcode 8 beta 6 dengan preferensi untuk:func foo<T: UIViewController>(vc:T) where T:UITableViewDataSource { ... }
LOP_Luke

29

Di Swift 4 Anda dapat mencapai ini dengan & tanda baru:

let vc: UIViewController & UITableViewDataSource

17

Dokumentasi buku Swift menyarankan agar Anda menggunakan batasan tipe dengan klausa where:

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

Ini menjamin bahwa "inParam" adalah jenis "SomeClass" dengan syarat bahwa ia juga mematuhi "SomeProtocol". Anda bahkan memiliki kekuatan untuk menentukan beberapa klausa yang dipisahkan oleh koma:

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

1
Tautan ke dokumentasi pasti menyenangkan untuk dilihat.
Raj

4

Dengan Swift 3, Anda dapat melakukan hal berikut:

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

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

1
Ini hanya berlaku untuk protokol, bukan protokol & kelas di swift 3.
Artem Goryaev


2

Bagaimana dengan cara ini ?:

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

Catatan pada September 2015 : Ini adalah pengamatan di hari-hari awal Swift.

Sepertinya tidak mungkin. Apple juga mengalami gangguan ini di beberapa API mereka. Berikut adalah salah satu contoh dari kelas yang baru diperkenalkan di iOS 8 (mulai beta 5):

UIInputViewController's textDocumentProxyproperti:

Didefinisikan dalam Objective-C sebagai berikut:

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

dan di Swift:

var textDocumentProxy: NSObject! { get }

Tautan ke dokumentasi Apple: https://developer.apple.com/library/prerelease/iOS/documentation/UIKit/Reference/UIInputViewController_Class/index.html#//apple_ref/occ/instp/UIInputViewController/textDocumentProxy


1
Ini tampaknya dibuat secara otomatis: Protokol Swift dapat diedarkan sebagai objek. Secara teoritis mereka bisa mengetikvar textDocumentProxy: UITextDocumentProxy! { get }
atlex2

@ atlex2 Anda telah kehilangan jenis kelas NSObject yang mendukung jenis protokol UITextDocumentProxy.
titaniumdecoy

@titaniumdecoy Tidak, Anda salah; Anda masih memiliki NSObject jika UITextDocumentProxy dideklarasikan seperti kebanyakan protokol:@protocol MyAwesomeCallbacks <NSObject>
CommaToast

@CommaToast Tidak dalam Swift, yang menjadi pokok pertanyaan ini.
titaniumdecoy

@titaniumdecoy Ya, Anda pada awalnya benar. Saya bingung! Maaf untuk mengatakan Anda salah. Sisi baiknya, Anda masih memiliki NSObjectProtocol ... dalam hal ini ... tapi saya tahu itu bukan hal yang sama.
CommaToast
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.