Wie kann ich in 'pure' Swift (ohne @objc) eine schwache Protokollreferenz erstellen?

561

weakReferenzen scheinen in Swift nur zu funktionieren, wenn a protocolals deklariert ist@objc , was ich in einer reinen Swift-App nicht möchte.

Dieser Code gibt einen Kompilierungsfehler aus ( weakkann nicht auf Nicht-Klassentypen angewendet werden MyClassDelegate):

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}

Ich muss dem Protokoll ein Präfix voranstellen @objc, dann funktioniert es.

Frage: Was ist der "reine" schnelle Weg, um eine zu erreichen weak delegate?

hnh
quelle
Beachten Sie ... stackoverflow.com/a/60837041/294884
Fattie

Antworten:

1038

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

protocol ProtocolNameDelegate: AnyObject {
    // Protocol stuff goes here
}

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

Wenn AnyObjectSie sagen, dass nur Klassen diesem Protokoll entsprechen können, können Strukturen oder Aufzählungen dies nicht.

Flainez
quelle
25
Mein Problem bei diesen Lösungen ist, dass das Aufrufen des Delegaten einen Absturz verursacht - EXC_BAD_ACCESS (wie von anderen an anderer Stelle angegeben). Dies scheint ein Fehler zu sein. Die einzige Lösung, die ich gefunden habe, besteht darin, @objc zu verwenden und alle Swift-Datentypen aus dem Protokoll zu entfernen.
Jim T
12
Was ist der richtige Weg, um schwache Delegierte jetzt in Swift zu erledigen? In der Apple-Dokumentation wird der Delegat in seinem Beispielcode nicht als schwach
angezeigt
2
Dies ist nicht immer sicher - denken Sie daran, dass Sie den Delegaten nur dann schwach machen müssen, wenn er auch einen Verweis auf den Delegator enthält, und dass Sie diesen starken Referenzzyklus unterbrechen müssen. Wenn der Delegat keinen Verweis auf den Delegator enthält, kann der Delegat den Gültigkeitsbereich verlassen (weil er schwach ist) und es kommt zu Abstürzen und anderen Problemen: / etwas, das Sie beachten sollten.
Trev14
5
Übrigens: Ich denke, der "neue Stil" (Swift 5) ist zu tun protocol ProtocolNameDelegate: AnyObject, spielt aber keine Rolle.
hnh
1
Es sollte AnyObjectda classirgendwann veraltet sein.
José
283

Ergänzende Antwort

Ich war immer verwirrt darüber, ob die Delegierten schwach sein sollten oder nicht. Vor kurzem habe ich mehr über Delegierte und die Verwendung schwacher Referenzen erfahren. Lassen Sie mich hier einige zusätzliche Punkte hinzufügen, um zukünftige Zuschauer zu unterstützen.

  • Der Zweck der Verwendung des weakSchlüsselworts besteht darin, starke Referenzzyklen zu vermeiden (Zyklen beizubehalten). Starke Referenzzyklen treten auf, wenn zwei Klasseninstanzen starke Referenzen zueinander haben. Ihre Referenzzählungen gehen niemals auf Null, sodass sie niemals freigegeben werden.

  • Sie müssen nur verwenden, weakwenn der Delegat eine Klasse ist. Schnelle Strukturen und Aufzählungen sind Werttypen (ihre Werte werden kopiert, wenn eine neue Instanz erstellt wird), keine Referenztypen, sodass sie keine starken Referenzzyklen erstellen.

  • weakReferenzen sind immer optional (andernfalls würden Sie sie verwenden unowned) und immer var(nicht let), damit die Option auf gesetzt werden kannnil wenn die Zuordnung aufgehoben wird.

  • Eine übergeordnete Klasse sollte natürlich einen starken Verweis auf ihre untergeordneten Klassen haben und daher das weakSchlüsselwort nicht verwenden . Wenn ein Kind jedoch einen Verweis auf seinen Elternteil wünscht, sollte es ihn mithilfe des weakSchlüsselworts zu einem schwachen Verweis machen .

  • weaksollte verwendet werden, wenn Sie einen Verweis auf eine Klasse wünschen, die Sie nicht besitzen, nicht nur für ein Kind, das auf sein Elternteil verweist. Wenn zwei nicht hierarchische Klassen aufeinander verweisen müssen, wählen Sie eine, die schwach ist. Welche Sie wählen, hängt von der Situation ab. Weitere Informationen hierzu finden Sie in den Antworten auf diese Frage .

  • In der Regel sollten Delegierte als gekennzeichnet werden,weak da die meisten Delegaten auf Klassen verweisen, die sie nicht besitzen. Dies ist definitiv der Fall, wenn ein Kind einen Delegierten verwendet, um mit einem Elternteil zu kommunizieren. In der Dokumentation wird empfohlen, eine schwache Referenz für den Delegaten zu verwenden . (Aber sieh das auch.)

  • Protokolle können für beide verwendet werden Referenztypen (Klassen) und Werttypen (structs, Aufzählungen). In dem wahrscheinlichen Fall, dass Sie einen Delegaten schwach machen müssen, müssen Sie ihn zu einem Nur-Objekt-Protokoll machen. Die Möglichkeit dazu besteht darin AnyObject, die Vererbungsliste des Protokolls zu erweitern. (In der Vergangenheit haben Sie dies mit dem classSchlüsselwort getan , AnyObjectjetzt wird es jedoch 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 dieunowned Schlüsselwort und die starken Referenzzyklen, die bei Schließungen auftreten.

verbunden

Suragch
quelle
5
Das ist alles schön und interessant, hat aber nichts mit meiner ursprünglichen Frage zu tun - es geht weder um Schwach / ARC selbst noch darum, warum Delegierte normalerweise schwach sind. Wir wissen bereits alles und haben uns nur gefragt, wie Sie eine schwache Protokollreferenz deklarieren können (von @flainez perfekt beantwortet).
hnh
30
Du hast recht. Ich hatte tatsächlich die gleiche Frage wie Sie zuvor, aber mir fehlten viele dieser Hintergrundinformationen. Ich habe die obige Lektüre durchgeführt und die ergänzenden Notizen gemacht, um mir zu helfen, alle Probleme zu verstehen, die mit Ihrer Frage zusammenhängen. Jetzt denke ich, ich kann Ihre akzeptierte Antwort anwenden und wissen, warum ich es tue. Ich hoffe, dass es vielleicht auch zukünftigen Zuschauern hilft.
Suragch
5
Aber kann ich ein schwaches Protokoll haben, das NICHT vom Typ abhängt? Einem Protokoll selbst ist es egal, welches Objekt sich selbst anpasst. So kann sich sowohl eine Klasse als auch eine Struktur daran anpassen. Ist es möglich, immer noch den Vorteil zu haben, dass beide sich daran anpassen können, aber nur die Klassentypen, die sich anpassen, schwach sind?
FlowUI. SimpleUITesting.com
> Da die meisten Delegaten auf Klassen verweisen, die sie nicht besitzen, würde ich dies wie folgt umschreiben: Die meisten Delegatoren. Andernfalls wird das nicht im Besitz befindliche Objekt Eigentümer
Victor Jalencas
36

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

class MyClass {
    weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate: AnyObject {
}

Von Apple:

Um starke Referenzzyklen zu vermeiden, sollten Delegierte als schwache Referenzen deklariert werden. Weitere Informationen zu schwachen Referenzen finden Sie unter Starke Referenzzyklen zwischen Klasseninstanzen. Wenn Sie das Protokoll als nur Klasse markieren, können Sie später erklären, dass der Delegat eine schwache Referenz verwenden muss. Sie markieren ein Protokoll als rein klassenbezogen, indem Sie von AnyObject erben , , wie unter Nur-Klassen-Protokolle beschrieben.

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

Tim Chen
quelle
7
Interessant. Ist classin Swift 4.1 veraltet?
hnh
@hnh Sie können immer noch ein "Pseudoprotokoll" erstellen, indem Sie es zu einer Klasse machen, aber Protokoll: AnyObject macht genau das, was das OP verlangt, mit weniger Nebenwirkungen als es zu einer Klasse zu machen. (Sie können ein solches Protokoll immer noch nicht mit
Werttypen
8

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

Original: Die Verwendung von @objc ist der richtige Weg, auch wenn Sie nicht mit Obj-C zusammenarbeiten. Es stellt sicher, dass Ihr Protokoll auf eine Klasse und nicht auf eine Aufzählung oder Struktur angewendet wird. Siehe "Überprüfen der Protokollkonformität" im Handbuch.

William Rust
quelle
Wie bereits erwähnt, ist dies IMO keine Antwort auf die Frage. Ein einfaches Swift-Programm sollte in der Lage sein, für sich allein zu stehen, ohne an NS'ism gebunden zu sein (dies könnte bedeuten, dass keine Delegierten mehr verwendet werden, sondern ein anderes Designkonstrukt). Meiner reinen Swift MyClass ist es eigentlich egal, ob das Ziel eine Struktur oder ein Objekt ist, noch benötige ich Optionen. Vielleicht können sie es später beheben, es ist doch eine neue Sprache. Möglicherweise so etwas wie 'Klassenprotokoll XYZ', wenn Referenzsemantik erforderlich ist?
hnh
4
Ich denke, es ist auch erwähnenswert, dass \ @objc zusätzliche Nebenwirkungen hat - der NSObjectProtocol-Vorschlag von @eXhausted ist etwas besser. Mit \ @objc - Wenn der Klassendelegierte ein Objektargument wie 'handleResult (r: MySwiftResultClass)' verwendet, muss die MySwiftResultClass jetzt von NSObject erben! Und vermutlich ist es auch kein Namespace mehr usw. Kurz gesagt: \ @objc ist eine Überbrückungsfunktion, keine Sprachfunktion.
hnh
Ich denke, sie haben das gelöst. Sie schreiben jetzt: Protokoll MyClassDelegate: class {}
user3675131
Wo ist die Dokumentation dazu? Entweder bin ich blind oder mache etwas falsch, weil ich keine Informationen darüber finden kann ... O_O
BastiBen
Ich bin nicht sicher, ob es die Frage des OP beantwortet oder nicht, aber dies ist besonders hilfreich, wenn Sie mit Objc-C zusammenarbeiten;)
Dan Rosenstark
-1

Das Protokoll muss eine Unterklasse der AnyObject-Klasse sein

Beispiel unten angegeben

    protocol NameOfProtocol: class {
   // member of protocol
    }
   class ClassName: UIViewController {
      weak var delegate: NameOfProtocol? 
    }
Rakesh Kunwar
quelle
-9

Apple verwendet "NSObjectProtocol" anstelle von "class".

public protocol UIScrollViewDelegate : NSObjectProtocol {
   ...
}

Dies funktioniert auch für mich und hat die Fehler beseitigt, die beim Versuch, mein eigenes Delegatenmuster zu implementieren, aufgetreten sind.

Michael Rose
quelle
5
Diese für die Frage nicht relevante Frage bezieht sich auf das Erstellen einer reinen Swift-Klasse (insbesondere kein NSObject), die ein Delegatenobjekt unterstützt. Es geht nicht darum, Objective-C-Protokolle zu implementieren, was Sie tun. Letzteres erfordert @objc aka NSObjectProtocol.
hnh
OK, aber nicht zu empfehlen.
DawnSong