Jak wymagać, aby protokół mógł zostać przyjęty tylko przez określoną klasę


91

Chcę ten protokół:

protocol AddsMoreCommands {
     /* ... */
}

tylko do przyjęcia przez klasy, które dziedziczą po klasie UIViewController. Ta strona mówi mi, że mogę określić, że jest ona przyjmowana tylko przez klasę (w przeciwieństwie do struktury), pisząc

protocol AddsMoreCommands: class {
}

ale nie rozumiem, jak wymagać, aby została przyjęta tylko przez określoną klasę. Ta strona mówi później o dodawaniu whereklauzul do rozszerzeń protokołu w celu sprawdzenia zgodności, ale nie widzę też, jak to dostosować.

extension AddsMoreCommands where /* what */ {
}

Czy jest na to sposób? Dzięki!

Odpowiedzi:


115
protocol AddsMoreCommands: class {
    // Code
}

extension AddsMoreCommands where Self: UIViewController {
    // Code
}

4
Miałem prawie to ... selfzamiast tego napisałem Self:-( Dziękuję bardzo, działa dobrze!
emrys57

Dla mnie powoduje to pewną dziwność składniową, gdy używam tego w połączeniu z rzutowaniem.
Chris Prince,

3
To nie zadziała, jeśli musisz dołączyć właściwość do protokołu, ponieważ rozszerzenia nie mogą zawierać przechowywanych właściwości.
shim

1
Może przechowywać własność, której potrzebujesz tylko: objc_getAssociatedObject (self, & KeyName) as? PropertyType
Michał Ziobro

Wymaga to również rzutowania, gdy deklaracja let / var ma typ, AddsMoreCommandsale metoda, którą przekazujesz, oczekujeUIViewController
GoatInTheMachine

80

Można to również osiągnąć bez przedłużenia:

protocol AddsMoreCommands: class where Self: UIViewController {
   // code
}

ZMIENIONO 2017/11/04 : Jak zauważył Zig , wydaje się to generować ostrzeżenie w Xcode 9.1. Obecnie w projekcie Swift (SR-6265) zgłoszono problem dotyczący usunięcia ostrzeżenia, będę go obserwować i odpowiednio aktualizować odpowiedź.

EDYTOWANE 2018/09/29 : classjest potrzebne, jeśli zmienna, która będzie przechowywać instancję, musi być słaba (na przykład delegat). Jeśli nie potrzebujesz słabej zmiennej, możesz pominąć classi po prostu napisać co następuje, a nie będzie żadnego ostrzeżenia:

protocol AddsMoreCommands where Self: UIViewController {
   // code
}

5
Jak przypadkowo kliknąłem na pytanie sprzed dwóch lat i znalazłem idealne rozwiązanie opublikowane godzinę temu 😲
Oscar Apeland

Xcode 9.1 daje mi teraz ostrzeżenie o tym powiedzeniu: Nadmiarowe ograniczenie układu „Self”: „AnyObject”. Ograniczenie układu „Self”: „AnyObject” implikowane w tym miejscu. Zmiana mojego kodu na format zaakceptowanej odpowiedzi wydaje się lepsza.
Zig

2
Począwszy od wersji Xcode 9.1 protokoły tylko klasy używają teraz AnyObjectzamiast class. protocol AddsMoreCommands: AnyObject where Self: UIViewController { // code }
dodgio

@dodgio nadal otrzymuje to samo ostrzeżenieAnyObject
rgkobashi

1
@ DávidPásztor masz rację, jednak jeśli chcesz użyć go we wzorcu strukturalnym, takim jak delegacja, aby móc osłabić właściwość, konieczne jest wyraźne dodanie „class” :)
rgkobashi

48

Z powodu problemu w poprzedniej odpowiedzi skończyłem z taką deklaracją:

protocol AddsMoreCommands where Self : UIViewController { 
    // protocol stuff here  
}

brak ostrzeżeń w Xcode 9.1


5
Popraw mnie, jeśli się mylę, ale problem z powyższym rozwiązaniem (generującym ostrzeżenie w Xcode 9.1 i nowszych wersjach) jest taki, że nie możesz zadeklarować delegata jako słabego?
Kyle Goslan

Ponadto, kiedy używam tego rozwiązania w Swift 4.1, muszę rzutować właściwości tego, AddsMoreCommandsdo UIViewControllerktórego chciałem uniknąć ...
fl034

9
Aby uniknąć obsady typu, możesz to zrobić:typealias AddsMoreCommandsViewController = UIViewController & AddsMoreCommands
plu

35

Teraz w Swift 5 możesz to osiągnąć poprzez:

protocol AddsMoreCommands: UIViewController {
     /* ... */
}

Całkiem przydatne.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.