Swift'de fonksiyon parametresi olarak protokole uygun sınıf


91

Objective-C'de, bir protokole uyan bir sınıfı, bir yöntem parametresi olarak belirlemek mümkündür. Örneğin, yalnızca aşağıdakilere UIViewControlleruyan bir yönteme izin verebilirim UITableViewDataSource:

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

Swift'de bunu yapmanın bir yolunu bulamıyorum (belki de henüz mümkün değildir). Kullanarak birden çok protokol belirtebilirsiniz func foo(obj: protocol<P1, P2>), ancak nesnenin belirli bir sınıfta olmasını nasıl şart koşabilirsiniz?


MyViewControllerClass gibi özel bir sınıf oluşturabilir ve bu sınıfın ilgilendiğiniz protokole uygun olduğundan emin olabilirsiniz. Sonra bağımsız değişkenin bu özel sınıfı kabul ettiğini bildirin. Her durumda işe yaramayacağının farkındayım ama bu bir yol ... yine de sorunuza bir cevap değil. Daha çok bir çözüm.
CommaToast

Yanıtlar:


134

fooGenel bir işlev olarak tanımlayabilir ve hem bir sınıf hem de bir protokol gerektirmek için tür kısıtlamalarını kullanabilirsiniz.

Swift 4

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

Swift 3 (Swift 4 için de geçerli)

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
Bunun gerekli olması biraz talihsizlik bence. Umarım gelecekte bunun için protocol<>sağlar (ancak protocol<>protokol olmayan türleri içeremez) gibi daha temiz bir sözdizimi olacaktır .
jtbandes

Bu beni çok üzüyor.
DCMaxxx

Merak ettiğim numberOfSectionsInTableViewiçin, gerekli bir işlev olduğu için açıkça açamaz UITableViewDataSourcemısın?
rb612

numberOfSectionsInTableView:isteğe bağlıdır — düşünüyor olabilirsiniz tableView:numberOfRowsInSection:.
Nate Cook

11
Swift 3'te bu, Xcode 8 beta 6'dan itibaren şu tercihlerle kullanımdan kaldırılmış gibi görünüyor:func foo<T: UIViewController>(vc:T) where T:UITableViewDataSource { ... }
LOP_Luke

29

Swift 4'te bunu yeni & işaretiyle başarabilirsiniz:

let vc: UIViewController & UITableViewDataSource

17

Swift kitap belgeleri, bir where cümlesiyle tür kısıtlamalarını kullanmanızı önerir:

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

Bu, "inParam" ın "SomeProtocol" a da bağlı olması koşuluyla "SomeClass" türünde olduğunu garanti eder. Hatta virgülle ayrılmış birden çok where cümlesi belirtme gücünüz bile var:

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

1
Belgelere bağlantı görmek güzel olurdu.
Raj

4

Swift 3 ile şunları yapabilirsiniz:

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

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

1
Bu sadece protokoller için geçerlidir, hızlı 3'teki protokol ve sınıf için geçerli değildir.
Artem Goryaev

3

Swift 5:

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

Yani esasen Jeroen'in yukarıdaki cevabı.


2

Peki ya bu yol ?:

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

Eylül 2015'teki not : Bu, Swift'in ilk günlerinde bir gözlemdi.

İmkansız gibi görünüyor. Apple'ın bazı API'lerinde de bu sıkıntısı var. İşte iOS 8'de yeni tanıtılan bir sınıftan bir örnek (beta 5'ten itibaren):

UIInputViewControllerbireyin textDocumentProxyözelliği:

Objective-C'de aşağıdaki gibi tanımlanmıştır:

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

ve Swift'de:

var textDocumentProxy: NSObject! { get }

Apple'ın belgelerine bağlantı: https://developer.apple.com/library/prerelease/iOS/documentation/UIKit/Reference/UIInputViewController_Class/index.html#//apple_ref/occ/instp/UIInputViewController/textDocumentProxy


1
Bu otomatik olarak oluşturulmuş görünüyor: Swift protokolleri nesneler olarak aktarılabilir. Teorik olarak sadece yazabilirlervar textDocumentProxy: UITextDocumentProxy! { get }
atlex2

@ atlex2 NSObject sınıf türünü UITextDocumentProxy protokol türü lehine kaybettiniz.
titaniumdecoy

@titaniumdecoy Hayır, yanılıyorsunuz; UITextDocumentProxy protokollerinin çoğu aşağıdaki gibi bildirilirse hala NSObject'e sahipsiniz:@protocol MyAwesomeCallbacks <NSObject>
CommaToast

@CommaToast Swift değil, bu soru da bu.
titaniumdecoy

@titaniumdecoy Evet, başlangıçta haklıydın. Kafam karışmıştı! Hatalı olduğunuzu söylediğim için üzgünüm. Başta NSObjectProtocol var ... bu örnekte ... ama aynı şey olmadığını biliyorum.
CommaToast
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.