คลาสที่สอดคล้องกับโปรโตคอลเป็นพารามิเตอร์ของฟังก์ชันใน Swift


91

ใน Objective-C สามารถระบุคลาสที่สอดคล้องกับโปรโตคอลเป็นพารามิเตอร์วิธีการได้ ตัวอย่างเช่นฉันสามารถมีวิธีการที่อนุญาตเฉพาะUIViewControllerที่สอดคล้องกับUITableViewDataSource:

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

ฉันหาวิธีทำใน Swift ไม่เจอ (อาจจะยังทำไม่ได้) คุณสามารถระบุหลายโปรโตคอลโดยใช้func foo(obj: protocol<P1, P2>)แต่คุณต้องการให้อ็อบเจ็กต์เป็นคลาสเฉพาะด้วยได้อย่างไร


คุณสามารถสร้างคลาสที่กำหนดเองได้เช่น MyViewControllerClass และตรวจสอบให้แน่ใจว่าคลาสนั้นสอดคล้องกับโปรโตคอลที่คุณสนใจ จากนั้นประกาศอาร์กิวเมนต์ยอมรับคลาสที่กำหนดเองนั้น ฉันรู้ว่ามันคงใช้ไม่ได้กับทุกสถานการณ์ แต่มันเป็นวิธี ... ไม่ใช่คำตอบสำหรับคำถามของคุณ วิธีแก้ปัญหาเพิ่มเติม
CommaToast

คำตอบ:


133

คุณสามารถกำหนด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
ในสวิฟท์ 3 นี้ดูเหมือนจะเลิกให้ Xcode 8 เบต้า 6 มีการตั้งค่าสำหรับ:func foo<T: UIViewController>(vc:T) where T:UITableViewDataSource { ... }
LOP_Luke


17

เอกสาร Swift book แนะนำให้คุณใช้ข้อ จำกัด ประเภทกับ where clause:

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
ลิงก์ไปยังเอกสารน่าจะดีที่ได้เห็น
Raj

4

ด้วย Swift 3 คุณสามารถทำสิ่งต่อไปนี้:

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

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

1
สิ่งนี้ใช้ได้กับโปรโตคอลเท่านั้นไม่ใช่โปรโตคอลและคลาสใน swift 3
Artem 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
}

0

หมายเหตุในเดือนกันยายน 2015 : นี่เป็นข้อสังเกตในช่วงแรก ๆ ของ Swift

ดูเหมือนจะเป็นไปไม่ได้ Apple มีความน่ารำคาญนี้ใน API บางตัวเช่นกัน นี่คือตัวอย่างหนึ่งจากคลาสที่เพิ่งเปิดตัวใน iOS 8 (ตั้งแต่เบต้า 5):

UIInputViewControllerของ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 Not in Swift ซึ่งเป็นคำถามเกี่ยวกับเรื่องนี้
titaniumdecoy

@titaniumdecoy ใช่คุณคิดถูกแล้ว ฉันสับสน! ขออภัยที่ต้องบอกว่าคุณผิด ในทางกลับกันคุณยังมี NSObjectProtocol ... ในกรณีนี้ ... แต่ฉันรู้ว่ามันไม่ใช่สิ่งเดียวกัน
CommaToast
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.