602 Stimmen

Wie kann ich eine schwache Protokollreferenz in "reinem" Swift (ohne @objc) erstellen?

weak Referenzen scheinen in Swift nicht zu funktionieren, es sei denn, ein protocol ist als @objc deklariert, was ich nicht in einer reinen Swift-App haben möchte.

Dieser Code erzeugt einen Kompilierfehler (weak kann nicht für den Nicht-Klassentyp MyClassDelegate angewendet werden):

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}

Ich muss das Protokoll mit @objc versehen, dann funktioniert es.

Frage: Was ist der 'reine' Swift-Weg, um einen weak delegate zu realisieren?

1134voto

flainez Punkte 11647

Sie müssen den Typ des Protokolls als AnyObject deklarieren.

protocol ProtocolNameDelegate: AnyObject {
    // Protokoll-Code hier einfügen
}

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

Indem Sie AnyObject verwenden, geben Sie an, dass nur Klassen diesem Protokoll entsprechen können, während Strukturen oder Aufzählungen dies nicht können.

326voto

Suragch Punkte 420096

Supplementäre Antwort

Ich war immer verwirrt, ob Delegierte schwach sein sollten oder nicht. Vor kurzem habe ich mehr über Delegierte und wann man schwache Referenzen verwenden sollte, gelernt, also möchte ich hier einige ergänzende Punkte hinzufügen, zum Wohle zukünftiger Betrachter.

  • Der Zweck der Verwendung des weak Schlüsselworts ist es, starke Referenzzyklen (Retain-Zyklen) zu vermeiden. Starke Referenzzyklen treten auf, wenn zwei Klasseninstanzen starke Referenzen zueinander haben. Ihre Referenzzähler gehen nie auf Null, daher werden sie nie deallokiert.

  • Du musst nur weak verwenden, wenn der Delegierte eine Klasse ist. Swift-Strukturen und Enums sind Werttypen (ihre Werte werden kopiert, wenn eine neue Instanz erstellt wird), nicht Referenztypen, daher erzeugen sie keine starken Referenzzyklen.

  • weak Referenzen sind immer optional (sonst würde man unowned verwenden) und immer mit var (nicht let) deklariert, damit das Optionale auf nil gesetzt werden kann, wenn es deallokiert wird.

  • Eine Elternklasse sollte naturgemäß eine starke Referenz zu ihren Kindklassen haben und daher nicht das Schlüsselwort weak verwenden. Wenn ein Kind jedoch eine Referenz zu seinem Elternteil möchte, sollte es eine schwache Referenz erstellen, indem es das Schlüsselwort weak verwendet.

  • weak sollte verwendet werden, wenn man eine Referenz zu einer Klasse haben möchte, die man nicht besitzt, nicht nur für ein Kind, das auf seinen Elternteil verweist. Wenn zwei nicht-hierarchische Klassen aufeinander verweisen müssen, sollte eine davon schwach sein. Welche man wählt, hängt von der Situation ab. Siehe die Antworten auf diese Frage für weitere Informationen dazu.

  • Als Faustregel sollten Delegierte als weak markiert werden, da die meisten Delegierten auf Klassen verweisen, die sie nicht besitzen. Das gilt definitiv, wenn ein Kind einen Delegierten verwendet, um mit einem Elternteil zu kommunizieren. Für den Delegierten eine schwache Referenz zu verwenden wird auch in der Dokumentation empfohlen. (Aber siehe auch das hier.)

  • Protokolle können für sowohl Referenztypen (Klassen) als auch Werttypen (Strukturen, Enums) verwendet werden. Daher, wenn du wahrscheinlich einen Delegierten schwach machen musst, musst du ein Objekt-Only-Protokoll erstellen. Der Weg dies zu tun besteht darin, AnyObject zur Vererbungsliste des Protokolls hinzuzufügen. (In der Vergangenheit hat man dies mit dem class Schlüsselwort gemacht, aber AnyObject ist jetzt bevorzugt.)

    protocol MyClassDelegate: AnyObject {
        // ...
    }
    
    class SomeClass {
        weak var delegate: MyClassDelegate?
    }

Weitere Studie

Das Lesen der folgenden Artikel hat mir geholfen, dies viel besser zu verstehen. Sie diskutieren auch verwandte Themen wie das unowned Schlüsselwort und die starken Referenzzyklen, die bei Closures auftreten.

Verwandt

40voto

Tim Chen Punkte 1296

AnyObject ist der offizielle Weg, um in Swift eine schwache Referenz zu verwenden.

class MyClass {
    weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate: AnyObject {
}

Von Apple:

Um starke Referenzzyklen zu verhindern, sollten Delegates als schwache Referenzen deklariert werden. Mehr Informationen zu schwachen Referenzen finden Sie unter Starke Referenzzyklen zwischen Klasseninstanzen. Das Markieren des Protokolls als nur für Klassen wird es Ihnen später ermöglichen zu deklarieren, dass der Delegate eine schwache Referenz verwenden muss. Sie markieren ein Protokoll als nur für Klassen, indem sie von AnyObject erben, wie in Klassen-Only-Protokollen diskutiert.

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID276

8voto

William Rust Punkte 640

Aktualisierung: Es sieht so aus, als wurde das Handbuch aktualisiert und das Beispiel, auf das ich mich bezog, wurde entfernt. Siehe die Bearbeitung von @flainez's Antwort oben.

Ursprünglich: Die Verwendung von @objc ist der richtige Weg, dies zu tun, auch wenn Sie nicht mit Obj-C interoperieren. Es stellt sicher, dass Ihr Protokoll auf eine Klasse angewendet wird und nicht auf ein Enum oder eine Struktur. Siehe "Überprüfen auf Protokollkonformität" im Handbuch.

1voto

Duncan C Punkte 121623

Der weak-Qualifizierer gilt nur für Referenz-Objekte. Wenn Sie nicht den @objc, AnyObject oder class Qualifizierer zu Ihrem Protokoll hinzufügen, könnte das Objekt, das dem Protokoll entspricht, kein Referenz-Objekt sein.

Daher benötigen Sie einen dieser Qualifizierer (und AnyObject wird empfohlen, da class veraltet ist.)

Übrigens, beachten Sie, dass das Hinzufügen von @objc zu Ihren Klassen und Eigenschaften manchmal erforderlich ist, auch in "reinem Swift" Anwendungen. Es hat nichts mit Ihrer Entwicklungssprache zu tun. Es bewirkt, dass der Compiler Ihren Code in einer Weise erstellt, die mit der Objective-C-Laufzeit kompatibel ist, was für einige Betriebssystem-Schnittstellen erforderlich ist (beispielsweise Ziel/Aktion und altmodische Schlüsselpfade)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X