web-dev-qa-db-de.com

Sollen wir in Swift immer [nicht besessenes Selbst] als inneren Verschluss verwenden?

In der WWDC 2014-Sitzung 403 Intermediate Swift und Transkription gab es die folgende Folie

enter image description here

Der Sprecher sagte in diesem Fall, wenn wir [unowned self] dort nicht verwenden, wird es ein Speicherverlust sein. Bedeutet das, dass wir immer [unowned self] innerhalb des Verschlusses verwenden sollten?

In Zeile 64 von ViewController.Swift der Swift Wetter-App verwende ich [unowned self] nicht. Aber ich aktualisiere die Benutzeroberfläche mit einigen @IBOutlets wie self.temperature und self.loadingIndicator. Es kann in Ordnung sein, weil alle @IBOutlets, die ich definiert habe, weak sind. Aber aus Sicherheitsgründen sollten wir immer [unowned self] verwenden?

class TempNotifier {
  var onChange: (Int) -> Void = {_ in }
  var currentTemp = 72
  init() {
    onChange = { [unowned self] temp in
      self.currentTemp = temp
    }
  }
}
445
Jake Lin

Nein, es gibt definitiv Zeiten, in denen Sie [unowned self] nicht verwenden möchten. Manchmal möchten Sie, dass der Verschluss sich selbst erfasst, um sicherzustellen, dass er zum Zeitpunkt des Aufrufs des Verschlusses noch vorhanden ist.

Beispiel: Eine asynchrone Netzwerkanfrage stellen

Wenn Sie eine asynchrone Netzwerkanforderung stellen, möchten Sie do , dass der Abschluss self beibehält, wenn die Anforderung abgeschlossen ist. Dieses Objekt wurde möglicherweise anderweitig freigegeben, Sie möchten jedoch weiterhin in der Lage sein, die Bearbeitung der Anforderung durchzuführen.

Wann ist unowned self oder weak self zu verwenden?

Das einzige Mal, wo Sie wirklich [unowned self] oder [weak self] verwenden möchten, ist, wenn Sie einen starken Referenzzyklus erstellen würden. Ein starker Referenzzyklus liegt vor, wenn es eine Eigentumsschleife gibt, in der Objekte sich gegenseitig besitzen (möglicherweise durch Dritte) und daher niemals freigegeben werden, da beide sicherstellen, dass sie sich gegenseitig halten.

Im speziellen Fall eines Abschlusses müssen Sie lediglich erkennen, dass jede Variable, auf die in ihm verwiesen wird, dem Abschluss "gehört". Solange der Verschluss in der Nähe ist, sind diese Objekte garantiert in der Nähe. Die einzige Möglichkeit, diesen Besitz zu beenden, besteht darin, den [unowned self] oder [weak self] auszuführen. Wenn also eine Klasse einen Abschluss besitzt und dieser Abschluss einen starken Bezug zu dieser Klasse erfasst, haben Sie einen starken Bezugszyklus zwischen dem Abschluss und der Klasse. Dies schließt auch ein, wenn die Klasse etwas besitzt, das den Abschluss besitzt.

Speziell im Beispiel aus dem Video

Im Beispiel auf der Folie besitzt TempNotifier den Abschluss über die Membervariable onChange. Wenn sie self nicht als unowned deklarieren, würde der Abschluss auch self besitzen und einen starken Referenzzyklus erzeugen.

Unterschied zwischen unowned und weak

Der Unterschied zwischen unowned und weak besteht darin, dass weak als optional deklariert wird, während unowned dies nicht ist. Indem Sie es weak deklarieren, können Sie den Fall behandeln, dass es sich irgendwann nicht mehr innerhalb des Verschlusses befindet. Wenn Sie versuchen, auf eine Variable unowned zuzugreifen, die zufällig null ist, stürzt das gesamte Programm ab. Verwenden Sie unowned nur, wenn Sie sicher sind, dass die Variable immer vorhanden ist, während der Abschluss aktiv ist

831
drewag

Update 11/2016

Ich habe einen Artikel darüber geschrieben, in dem diese Antwort erweitert wird (siehe SIL, um zu verstehen, was ARC tut). Schauen Sie sich dies hier an .

Ursprüngliche Antwort

Die vorherigen Antworten enthalten keine klaren Regeln darüber, wann und warum eine übereinander verwendet werden soll. Lassen Sie mich einige Dinge hinzufügen.

Die unbekannte oder schwache Diskussion läuft auf eine Frage der Lebensdauer der Variablen und des darauf bezogenen Abschlusses hinaus.

Swift weak vs unowned

Szenarien

Sie können zwei mögliche Szenarien haben:

  1. Der Abschluss hat dieselbe Lebensdauer der Variablen, sodass der Abschluss nur so lange erreichbar ist , bis die Variable erreichbar ist . Die Variable und der Verschluss haben die gleiche Lebensdauer. In diesem Fall sollten Sie die Referenz als nicht im Besitz deklarieren. Ein häufiges Beispiel ist der [unowned self], der in vielen Beispielen für kleine Abschlüsse verwendet wird, die etwas im Kontext ihrer Eltern tun und auf die nirgendwo anders verwiesen wird, was ihre Eltern nicht überlebt.

  2. Die Lebensdauer der Schließung ist unabhängig von der der Variablen. Die Schließung kann immer noch referenziert werden, wenn die Variable nicht mehr erreichbar ist. In diesem Fall sollten Sie die Referenz als schwach deklarieren und sicherstellen, dass sie nicht null ist, bevor Sie sie verwenden (nicht mit Gewalt auspacken). Ein häufiges Beispiel hierfür ist der [weak delegate], den Sie in einigen Beispielen für das Schließen sehen, wenn Sie auf ein (lebenslängliches) Delegatenobjekt verweisen, das in keiner Beziehung steht.

Tatsächliche Nutzung

Also, welche werden/sollten Sie eigentlich die meiste Zeit verwenden?

Joe Groff aus Twitter zitieren :

Unown ist schneller und lässt Unveränderlichkeit und Nichtoptionalität zu.

Wenn Sie keine schwachen brauchen, verwenden Sie es nicht.

Weitere Informationen zu "unowned*" finden Sie hier .

* Wird normalerweise auch als "nicht im Besitz" (sicher) bezeichnet, um anzuzeigen, dass vor dem Zugriff auf die nicht im Besitz befindliche Referenz Laufzeitprüfungen durchgeführt werden (die zu einem Absturz bei ungültigen Referenzen führen).

177

Ich dachte, ich würde einige konkrete Beispiele speziell für einen View-Controller hinzufügen. Viele der Erklärungen, nicht nur hier auf Stack Overflow, sind wirklich gut, aber ich arbeite besser mit Beispielen aus der Praxis (@drewag hatte einen guten Anfang dafür):

  • Wenn Sie einen Abschluss haben, um eine Antwort von Netzwerkanforderungen zu verarbeiten, verwenden Sie weak, da diese langlebig sind. Der Ansichtscontroller könnte geschlossen werden, bevor die Anforderung abgeschlossen ist, sodass self nicht mehr auf ein gültiges Objekt verweist, wenn der Abschluss aufgerufen wird.
  • Wenn Sie einen Abschluss haben, der ein Ereignis auf einer Schaltfläche verarbeitet. Dies kann unowned sein, da die Schaltfläche und alle anderen Elemente, auf die von self verwiesen wird, gleichzeitig ausgeblendet werden, sobald der Ansichts-Controller nicht mehr angezeigt wird. Gleichzeitig verschwindet auch der Verschlussblock.

    class MyViewController: UIViewController {
          @IBOutlet weak var myButton: UIButton!
          let networkManager = NetworkManager()
          let buttonPressClosure: () -> Void // closure must be held in this class. 
    
          override func viewDidLoad() {
              // use unowned here
              buttonPressClosure = { [unowned self] in
                  self.changeDisplayViewMode() // won't happen after vc closes. 
              }
              // use weak here
              networkManager.fetch(query: query) { [weak self] (results, error) in
                  self?.updateUI() // could be called any time after vc closes
              }
          }
          @IBAction func buttonPress(self: Any) {
             buttonPressClosure()
          }
    
          // rest of class below.
     }
    
84
possen

Wenn self im Closure null sein könnte, verwende [weak self] .

Wenn self niemals null sein wird, verwende [nicht besessenes self] .

Die Apple Swift Dokumentation enthält einen großartigen Abschnitt mit Bildern, die den Unterschied zwischen der Verwendung von strong , weak und nicht besessen in Verschlüssen:

https://developer.Apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html

67
TenaciousJay

Hier sind brillante Zitate aus Apple Developer Forums köstlichen Details beschrieben:

unowned vs unowned(safe) vs unowned(unsafe)

unowned(safe) ist eine nicht-besitzende Referenz, die beim Zugriff bestätigt, dass das Objekt noch lebt. Es ist eine Art schwacher optionaler Verweis, der bei jedem Zugriff implizit mit x! entpackt wird. unowned(unsafe) ähnelt __unsafe_unretained in ARC - es handelt sich um eine nicht im Besitz befindliche Referenz, es wird jedoch nicht zur Laufzeit überprüft, ob das Objekt beim Zugriff noch aktiv ist, sodass baumelnde Referenzen in den Garbage Memory gelangen. unowned ist derzeit immer ein Synonym für unowned(safe), aber die Absicht ist, dass es für unowned(unsafe) in -Ofast-Builds optimiert wird, wenn Laufzeitprüfungen deaktiviert sind.

unowned vs weak

unowned verwendet tatsächlich eine viel einfachere Implementierung als weak. Native Swift -Objekte weisen zwei Referenzzählwerte auf, und bei unowned -Referenzen wird der nicht verwendete Referenzzählwert anstelle der starken Referenzzahl . Das Objekt wird deinitialisiert, wenn sein starker Referenzzähler Null erreicht, die Zuordnung jedoch erst aufgehoben wird Der nicht besessene Referenzzähler erreicht ebenfalls Null. Dies führt dazu, dass der Speicher etwas länger gehalten wird, wenn nicht im Besitz befindliche Referenzen vorhanden sind. Dies ist jedoch normalerweise kein Problem, wenn unowned verwendet wird, da die zugehörigen Objekte ohnehin eine nahezu gleiche Lebensdauer haben sollten und viel einfacher und kostengünstiger sind als die Nebentabellen-basierte Implementierung zum Nullsetzen schwacher Referenzen.

Update: In modernen Swift weak VERWENDET INTERN DENSELBEN MECHANISMUS WIE unowned . Dieser Vergleich ist daher falsch, weil er Objective-Cweak mit Swiftunonwed vergleicht.

Gründe dafür

Welchen Zweck hat es, die Erinnerung am Leben zu erhalten, nachdem der Besitz von Referenzen 0 erreicht hat? Was passiert, wenn Code nach der Deinitialisierung versucht, etwas mit dem Objekt zu tun, indem er eine nicht im Besitz befindliche Referenz verwendet?

Der Speicher wird am Leben gehalten, so dass die Anzahl der Speicherplätze weiterhin verfügbar ist. Auf diese Weise kann die Laufzeit beim Versuch, einen starken Verweis auf das nicht verwendete Objekt beizubehalten, überprüfen, ob der Wert für den starken Verweis größer als Null ist, um sicherzustellen, dass das Objekt sicher aufbewahrt werden kann.

Was passiert mit dem Besitz oder Nichtbesitz von Referenzen, die sich im Besitz des Objekts befinden? Wird ihre Lebensdauer von dem Objekt entkoppelt, wenn es deinitialisiert wird, oder bleibt ihr Speicher auch erhalten, bis die Zuordnung des Objekts aufgehoben wird, nachdem die letzte nicht besessene Referenz freigegeben wurde?

Alle dem Objekt gehörenden Ressourcen werden freigegeben, sobald die letzte starke Referenz des Objekts freigegeben und dessen Deinit ausgeführt wird. Nicht besessene Referenzen halten nur den Speicher am Leben - abgesehen von der Kopfzeile mit den Referenzzählern ist der Inhalt Junk.

Aufgeregt, was?

48

Hier gibt es einige gute Antworten. Die jüngsten Änderungen an der Implementierung von schwachen Verweisen durch Swift sollten jedoch die Entscheidungen über das schwache Selbst im Vergleich zu Entscheidungen über die Nutzung ohne eigenen Eigentümer ändern. Wenn Sie zuvor die beste Leistung mit einem nicht besessenen Selbst benötigten, war dies dem schwachen Selbst überlegen, solange Sie sicher sein konnten, dass das Selbst niemals Null sein würde, da der Zugriff auf ein nicht besessenes Selbst viel schneller ist als der Zugriff auf ein schwaches Selbst.

Aber Mike Ash hat dokumentiert, wie Swift die Implementierung schwacher Vars für die Verwendung von Nebentabellen aktualisiert hat und wie dies die schwache Selbstleistung wesentlich verbessert.

https://mikeash.com/pyblog/friday-qa-2017-09-22-Swift-4-weak-references.html

Jetzt, da es keine signifikanten Performanceeinbußen für das schwache Selbst gibt, sollten wir es meines Erachtens künftig standardmäßig verwenden. Der Vorteil des schwachen Selbst ist, dass es optional ist, was das Schreiben von korrekterem Code wesentlich erleichtert. Dies ist im Grunde der Grund, warum Swift eine so großartige Sprache ist. Sie denken vielleicht, Sie wissen, welche Situationen für die Verwendung von nicht-besessenem Selbst sicher sind, aber meine Erfahrung beim Überprüfen vieler anderer Entwickler-Codes ist, dass die meisten dies nicht tun. Ich habe viele Abstürze behoben, bei denen nicht besessenes Ich freigegeben wurde, normalerweise in Situationen, in denen ein Hintergrundthread abgeschlossen wird, nachdem die Zuordnung eines Controllers aufgehoben wurde.

Fehler und Abstürze sind die zeitaufwändigsten, schmerzhaftesten und teuersten Teile der Programmierung. Gib dein Bestes, um korrekten Code zu schreiben und sie zu vermeiden. Ich empfehle, es zur Regel zu machen, niemals das Auspacken von Optionen zu erzwingen und niemals ein nicht besessenes Selbst anstelle eines schwachen Selbst zu verwenden. Sie werden nichts verlieren, wenn Sie die Zeiten verpassen, in denen das gewaltsame Auspacken und das nicht in Besitz genommene Selbst tatsächlich sicher sind. Aber Sie werden viel davon profitieren, schwer zu findende Abstürze und Bugs zu beseitigen und zu debuggen.

33

Laut Apple-doc

  • Schwache Referenzen sind immer optional und werden automatisch zu Null, wenn die Zuordnung der Instanz aufgehoben wird, auf die sie verweisen.

  • Wenn die erfasste Referenz niemals null wird, sollte sie immer als nicht besessene Referenz und nicht als schwache Referenz erfasst werden

Beispiel -

    // if my response can nil use  [weak self]
      resource.request().onComplete { [weak self] response in
      guard let strongSelf = self else {
        return
      }
      let model = strongSelf.updateModel(response)
      strongSelf.updateUI(model)
     }

    // Only use [unowned self] unowned if guarantees that response never nil  
      resource.request().onComplete { [unowned self] response in
      let model = self.updateModel(response)
      self.updateUI(model)
     }
3
Jack

Wenn keine der obigen Aussagen Sinn macht:

tl; dr

Genau wie bei einem implicitly unwrapped optional: Wenn Sie garantieren können, dass die Referenz an ihrem Verwendungsort nicht null ist, verwenden Sie sie ohne Eigentum. Wenn nicht, sollten Sie schwach verwenden.

Erklärung:

Ich habe die folgenden Informationen unter folgender Adresse abgerufen: schwach besessener Link . Nach allem, was ich zusammengetragen habe, kann nicht besessenes Ich kann nicht null sein, aber schwaches Ich kann sein, und nicht besessenes Ich kann zu baumelnden Hinweisen führen ... etwas Berüchtigtes in Objective-C. Ich hoffe es hilft

"UNOWNED Schwache und nicht besessene Referenzen verhalten sich ähnlich, sind aber NICHT gleich."

Nicht besessene Referenzen wie schwache Referenzen erhöhen nicht die Anzahl der Aufbewahrungen des Objekts, auf das verwiesen wird. In Swift hat ein nicht besessener Verweis jedoch den zusätzlichen Vorteil, kein optionaler zu sein. Dies macht sie einfacher zu verwalten anstatt auf optionale Bindungen zurückzugreifen. Dies ist nicht anders als bei implizit ausgepackten Optionen. Darüber hinaus sind nicht besessene Referenzen nicht nullend . Dies bedeutet, dass beim Aufheben der Zuordnung des Objekts der Zeiger nicht auf Null gesetzt wird. Dies bedeutet, dass die Verwendung von nicht im Besitz befindlichen Referenzen in einigen Fällen dazu führen kann, dass Zeiger hängen bleiben . Für Sie Nerds da draußen, die sich wie ich an die Objective-C-Tage erinnern, werden nicht besessene Referenzen unsicheren_unbehaltenen Referenzen zugeordnet.

Hier wird es etwas verwirrend.

Schwache und nicht besessene Referenzen erhöhen die Anzahl der Aufbewahrungen nicht.

Sie können beide verwendet werden, um Haltezyklen zu unterbrechen. Also wann benutzen wir sie ?!

Laut Apples Dokumenten :

„Verwenden Sie eine schwache Referenz, wenn sie gültig ist, damit diese Referenz zu einem bestimmten Zeitpunkt ihres Lebens null wird. Verwenden Sie umgekehrt eine nicht besessene Referenz, wenn Sie wissen, dass die Referenz niemals null sein wird, wenn sie während der Initialisierung festgelegt wurde. “

0
Dennis

Starke Referenzzyklen für Verschlüsse

Ein starker Referenzzyklus kann auch auftreten, wenn Sie einer Eigenschaft einer Klasseninstanz einen Abschluss zuweisen und der Hauptteil dieses Abschlusses die Instanz erfasst. Diese Erfassung kann auftreten, weil der Körper des Abschlusses auf eine Eigenschaft der Instanz zugreift, z. B. self.someProperty, oder weil der Abschluss eine Methode für die Instanz aufruft, z. B. self.someMethod(). In beiden Fällen führen diese Zugriffe dazu, dass der Abschluss self „erfasst“, wodurch ein starker Referenzzyklus entsteht. Weitere Informationen zu Erfassen von Werten in einem Closure

Swift bietet eine elegante Lösung für dieses Problem, die als Closure Capture List bezeichnet wird. Eine Erfassungsliste definiert die Regeln, die beim Erfassen eines oder mehrerer Referenztypen im Körper des Verschlusses verwendet werden sollen. Wie bei starken Referenzzyklen zwischen zwei Klasseninstanzen deklarieren Sie jede erfasste Referenz als weak- oder unowned -Referenz und nicht als strong -Referenz. Die richtige Auswahl von weak oder unowned hängt von den Beziehungen zwischen den verschiedenen Teilen Ihres Codes ab. mehr hier SO

  • Verwenden Sie eine weak -Referenz, wenn sie gültig ist, dass diese Referenz zu einem bestimmten Zeitpunkt während ihrer Lebensdauer nil wird.
  • Verwenden Sie eine unowned -Referenz, wenn Sie wissen, dass die Referenz niemals nil sein wird, wenn sie während der Initialisierung festgelegt wurde.

Doc mit Beispiel

0
yoAlex5