Wie man verlangt, dass ein Protokoll nur von einer bestimmten Klasse übernommen werden kann

88

Ich möchte dieses Protokoll:

protocol AddsMoreCommands {
     /* ... */
}

Nur von Klassen zu übernehmen, die von der Klasse erben UIViewController. Auf dieser Seite kann ich angeben, dass sie nur von einer Klasse (im Gegensatz zu einer Struktur) schriftlich übernommen wird

protocol AddsMoreCommands: class {
}

aber ich kann nicht sehen, wie ich verlangen soll, dass es nur von einer bestimmten Klasse übernommen wird. Auf dieser Seite wird später über das Hinzufügen von whereKlauseln zu Protokollerweiterungen gesprochen, um die Konformität zu überprüfen, aber ich kann auch nicht sehen, wie dies angepasst werden kann.

extension AddsMoreCommands where /* what */ {
}

Gibt es eine Möglichkeit, dies zu tun? Vielen Dank!

emrys57
quelle

Antworten:

113
protocol AddsMoreCommands: class {
    // Code
}

extension AddsMoreCommands where Self: UIViewController {
    // Code
}
Roee84
quelle
4
Ich hatte es so fast ... Ich schrieb selfstatt Self:-( Vielen Dank, das funktioniert gut!
emrys57
Für mich verursacht dies eine gewisse syntaktische Fremdheit, wenn ich dies in Verbindung mit Casting verwende.
Chris Prince
3
Dies funktioniert nicht, wenn Sie eine Eigenschaft in das Protokoll aufnehmen müssen, da Erweiterungen keine gespeicherten Eigenschaften enthalten können.
Shim
1
Es kann Eigentum gespeichert haben, das Sie nur verwenden müssen: objc_getAssociatedObject (self, & KeyName) as? PropertyType
Michał Ziobro
Dies erfordert auch Casting, wenn eine let / var-Deklaration einen Typ hat, AddsMoreCommandsaber eine Methode, an die Sie sie übergeben, einenUIViewController
GoatInTheMachine
77

Dies kann auch ohne Erweiterung erreicht werden:

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

EDITED 2017/11/04 : Wie Zig betonte, scheint dies eine Warnung auf Xcode 9.1 zu generieren. Derzeit wurde ein Problem mit dem Swift-Projekt gemeldet (SR-6265). , um die Warnung zu entfernen. Ich werde sie im Auge behalten und die Antwort entsprechend aktualisieren.

EDITED 2018/09/29 : classWird benötigt, wenn die Variable, in der die Instanz gespeichert wird, schwach sein muss (z. B. ein Delegat). Wenn Sie keine schwache Variable benötigen, können Sie die weglassen classund einfach Folgendes schreiben, und es wird keine Warnung angezeigt:

protocol AddsMoreCommands where Self: UIViewController {
   // code
}
rgkobashi
quelle
5
Wie zufällig ist es, dass ich auf eine zwei Jahre alte Frage geklickt habe und eine perfekte Lösung gefunden habe, die vor einer Stunde veröffentlicht wurde Oscar
Oscar Apeland
Xcode 9.1 warnt mich jetzt vor diesem Sprichwort: Redundante Layoutbeschränkung 'Self': 'AnyObject'. Layouteinschränkung Einschränkung 'Self': 'AnyObject' hier impliziert. Das Ändern meines Codes in das Format der akzeptierten Antwort scheint besser zu sein.
Zig
2
Ab Xcode 9.1 werden jetzt nur noch Klassenprotokolle AnyObjectanstelle von verwendet class. protocol AddsMoreCommands: AnyObject where Self: UIViewController { // code }
Dodgio
@ Dodgio immer noch die gleiche Warnung mitAnyObject
Rgkobashi
1
@ DávidPásztor Sie haben Recht, aber wenn Sie es für ein Strukturmuster wie Delegation verwenden möchten, um die Eigenschaft schwach zu machen, ist es notwendig, "Klasse" explizit hinzuzufügen :)
rgkobashi
48

Aufgrund eines Problems in der vorherigen Antwort kam ich zu folgender Erklärung:

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

Keine Warnungen in Xcode 9.1

Massenmacher
quelle
5
Korrigieren Sie mich, wenn ich falsch liege, aber das Problem mit der obigen Lösung (die in Xcode 9.1 und höher eine Warnung generiert) ist, dass Sie den Delegaten nicht als schwach deklarieren können.
Kyle Goslan
Wenn ich diese Lösung mit Swift 4.1 verwende, muss ich auch die Eigenschaften der AddsMoreCommandsUIViewController
Elemente umwandeln
8
Um die Typumwandlung zu vermeiden, können Sie dies tun:typealias AddsMoreCommandsViewController = UIViewController & AddsMoreCommands
plu
33

In Swift 5 können Sie dies erreichen, indem Sie:

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

Sehr praktisch.

Wojciech Kulik
quelle