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!

emrys57
źródło

Odpowiedzi:

115
protocol AddsMoreCommands: class {
    // Code
}

extension AddsMoreCommands where Self: UIViewController {
    // Code
}
Roee84
źródło
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
}
rgkobashi
źródło
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

Massmaker
źródło
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.

Wojciech Kulik
źródło