Was ist der Unterschied zwischen einer schwachen Referenz und einer nicht besessenen Referenz?


240

Swift hat:

  • Starke Referenzen
  • Schwache Referenzen
  • Nicht besessene Referenzen

Wie unterscheidet sich eine nicht besessene Referenz von einer schwachen Referenz?

Wann ist es sicher, eine nicht im Besitz befindliche Referenz zu verwenden?

Sind nicht besessene Referenzen ein Sicherheitsrisiko wie baumelnde Zeiger in C / C ++?


3
Sehr guter Artikel auf andrewcbancroft.com/2015/05/08/…
Zeeshan

Meine Erfahrung ist es, unownedfür die Klassen, die wir steuern, für Apple-Klassen zu verwenden, weakweil wir nicht sicher garantieren können, was es tut
onmyway133

@NoorAli oder "ownBy" als "nicht besessene" Referenz verweist häufig auf den Eigentümer.
Ian Ringrose

Antworten:


361

Sowohl weakals auch unownedReferenzen erzeugen keinen strongHalt für das referenzierte Objekt (auch bekannt als Erhöhung der Aufbewahrungsanzahl, um zu verhindern, dass ARC die Zuordnung des referenzierten Objekts aufhebt).

Aber warum zwei Schlüsselwörter? Diese Unterscheidung hat damit zu tun, dass OptionalTypen in der Swift-Sprache integriert sind. Lange Rede, kurzer Sinn: Optionale Typen bieten Speichersicherheit (dies funktioniert hervorragend mit den Konstruktorregeln von Swift, die streng sind, um diesen Vorteil zu bieten).

Eine weakReferenz ermöglicht die Möglichkeit, dass sie wird nil(dies geschieht automatisch, wenn die Zuordnung des referenzierten Objekts aufgehoben wird). Daher muss der Typ Ihrer Eigenschaft optional sein. Sie als Programmierer sind daher verpflichtet, diese zu überprüfen, bevor Sie sie verwenden (im Grunde genommen die Der Compiler zwingt Sie so weit wie möglich, sicheren Code zu schreiben.

Eine unownedReferenz geht davon aus, dass es zu nilLebzeiten niemals werden wird . Während der Initialisierung muss eine nicht besessene Referenz festgelegt werden. Dies bedeutet, dass die Referenz als nicht optionaler Typ definiert wird, der ohne Überprüfungen sicher verwendet werden kann. Wenn die Zuordnung des Objekts, auf das verwiesen wird, irgendwie aufgehoben wird, stürzt die App ab, wenn die nicht besessene Referenz verwendet wird.

Aus den Apple-Dokumenten :

Verwenden Sie eine schwache Referenz, wenn es gültig ist, dass diese Referenz irgendwann während ihrer Lebensdauer 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.

In den Dokumenten finden Sie einige Beispiele, in denen die Aufbewahrungszyklen und deren Unterbrechung erläutert werden. Alle diese Beispiele werden aus den Dokumenten extrahiert .

Beispiel für das weakSchlüsselwort:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
}

class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    weak var tenant: Person?
}

Und jetzt zu einigen ASCII-Grafiken (Sie sollten sich die Dokumente ansehen - sie haben hübsche Diagramme):

Person ===(strong)==> Apartment
Person <==(weak)===== Apartment

Das Beispiel Personund Apartmentzeigt eine Situation, in der zwei Eigenschaften, die beide gleich Null sein dürfen, möglicherweise einen starken Referenzzyklus verursachen. Dieses Szenario lässt sich am besten mit einer schwachen Referenz lösen. Beide Entitäten können existieren, ohne eine strikte Abhängigkeit voneinander zu haben.

Beispiel für das unownedSchlüsselwort:

class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) { self.name = name }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) { self.number = number; self.customer = customer }
}

In diesem Beispiel kann a ein Customerhaben oder nicht CreditCard, aber a CreditCard wird immer mit a verknüpft Customer. Um dies darzustellen, verfügt die CustomerKlasse über eine optionale cardEigenschaft, die CreditCardKlasse jedoch über eine nicht optionale (und nicht besessene) customerEigenschaft.

Customer ===(strong)==> CreditCard
Customer <==(unowned)== CreditCard

Das Beispiel Customerund CreditCardzeigt eine Situation, in der eine Eigenschaft, die Null sein darf, und eine andere Eigenschaft, die nicht Null sein darf, möglicherweise einen starken Referenzzyklus verursachen. Dieses Szenario lässt sich am besten mit einer nicht besessenen Referenz lösen.

Anmerkung von Apple:

Schwache Referenzen müssen als Variablen deklariert werden, um anzuzeigen, dass sich ihr Wert zur Laufzeit ändern kann. Eine schwache Referenz kann nicht als Konstante deklariert werden.

Es gibt auch ein drittes Szenario, in dem beide Eigenschaften immer einen Wert haben sollten und keine Eigenschaft nach Abschluss der Initialisierung jemals Null sein sollte.

Außerdem gibt es die klassischen Szenarien für den Beibehaltungszyklus, die beim Arbeiten mit Verschlüssen vermieden werden sollten.

Zu diesem Zweck empfehle ich Ihnen, die Apple-Dokumente zu besuchen oder das Buch zu lesen .


3
Dies ist etwas trivial, aber ich finde das Beispiel der Wohnung und der Person etwas verwirrend, was auch eine zusätzliche Lösung darstellt, um den starken Referenzzyklus zu durchbrechen. Die Wohnung einer Person ist optional und kann daher null sein, ebenso wie der Mieter einer Wohnung optional ist und daher null sein kann, sodass beide Eigenschaften als schwach definiert werden können. `` `
Justin Levi Winter

Klasse Person {Name lassen: String init (Name: String) {self.name = name} schwache var Wohnung: Wohnung? } class Apartment {let number: Int init (number: Int) {self.number = number} schwacher var Mieter: Person? }
Justin Levi Winter

3
Was ist der Unterschied zwischen weak var Person?vs. var Person??
Dean

4
@JustinLevi, Wenn Sie beide Eigenschaften als schwach deklarieren, besteht die Möglichkeit, dass sie freigegeben werden. Die Person behält einen starken Bezug zur Wohnung, so dass die Wohnung nicht freigegeben wird. Wenn die Wohnung den gleichen starken Bezug zur Person haben würde, würden sie einen Aufbewahrungszyklus erstellen - der vom Programmierer zur Laufzeit unterbrochen werden kann, wenn er davon weiß, aber ansonsten ist es nur ein Speicherverlust. Dies ist die ganze Aufregung um stark, schwach und unbesessen: Speicherverwaltung auf einer höheren Ebene, weil ARC all die schmutzigen Dinge für uns erledigt. Das Vermeiden von Retain-Zyklen ist unsere Aufgabe.
Ilea Cristian

1
Sind die einzigen Vorteile von nicht besessen gegenüber schwach, dass Sie nicht auspacken müssen und eine Konstante verwenden können? Gibt es einen Fall, in dem Sie nicht schwach und nur nicht besessen verwenden konnten?
Alan

29

Q1. Wie unterscheidet sich eine "nicht genannte Referenz" von einer "schwachen Referenz"?

Schwache Referenz:

Eine schwache Referenz ist eine Referenz, die die Instanz, auf die sie verweist, nicht festhält und ARC daher nicht daran hindert, die referenzierte Instanz zu entsorgen. Da schwache Referenzen keinen Wert haben dürfen, müssen Sie jede schwache Referenz als optionalen Typ deklarieren. (Apple Docs)

Unbesessene Referenz:

Wie schwache Referenzen behält eine nicht besessene Referenz die Instanz, auf die sie verweist, nicht fest im Griff. Im Gegensatz zu einer schwachen Referenz wird jedoch angenommen, dass eine nicht besessene Referenz immer einen Wert hat. Aus diesem Grund wird eine nicht besessene Referenz immer als nicht optionaler Typ definiert. (Apple Docs)

Wann verwenden Sie jedes:

Verwenden Sie eine schwache Referenz, wenn es gültig ist, dass diese Referenz irgendwann während ihrer Lebensdauer 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. (Apple Docs)


Q2. Wann ist es sicher, eine „nicht im Besitz befindliche Referenz“ zu verwenden?

Wie oben zitiert, wird angenommen, dass eine nicht besessene Referenz immer einen Wert hat. Sie sollten es also nur verwenden, wenn Sie sicher sind, dass die Referenz niemals Null sein wird. Apple Docs veranschaulichen anhand des folgenden Beispiels einen Anwendungsfall für nicht besessene Referenzen.

Angenommen, wir haben zwei Klassen Customerund CreditCard. Ein Kunde kann ohne Kreditkarte existieren, aber eine Kreditkarte wird nicht ohne Kunden existieren, dh es kann angenommen werden, dass eine Kreditkarte immer einen Kunden hat. Sie sollten also die folgende Beziehung haben:

class Customer {
    var card: CreditCard?
}

class CreditCard {
    unowned let customer: Customer
}

Q3. Sind "nicht besessene Referenzen" ein Sicherheitsrisiko wie "baumelnde Zeiger" in C / C ++?

Das glaube ich nicht.

Da nicht besessene Referenzen nur schwache Referenzen sind, deren Wert garantiert ist, sollte dies in keiner Weise ein Sicherheitsrisiko darstellen. Wenn Sie jedoch versuchen, auf eine nicht besessene Referenz zuzugreifen, nachdem die Zuweisung der Instanz, auf die sie verweist, freigegeben wurde, wird ein Laufzeitfehler ausgelöst und die App stürzt ab.

Das ist das einzige Risiko, das ich damit sehe.

Link zu Apple Docs


Ihr Q2-Beispielprogramm ist einfach zu verstehen über nicht besessen. Danke. Können Sie die gleiche Art von Beispiel für schwach und stark hinzufügen?
Ranjith Kumar

Ausgezeichnet. Danke dir.
Swifty McSwifterton

Können Sie ein allgemeines Beispiel für nicht besessen oder schwach angeben?
Honig

Betrachten Sie die Objekte parent & child. Wenn child ohne parent nicht existieren kann, verwenden Sie es unownedfür die Eigenschaft parent in der untergeordneten Klasse. schwach ist umgekehrt. Schöne Erklärung @myxtic! unownedReferenzen sind nur weakReferenzen, die garantiert einen Wert haben!
Saif

26

Wenn das Selbst im Verschluss gleich Null sein könnte, benutze [schwaches Selbst] .

Wenn das Selbst im Verschluss niemals Null sein wird, benutze [nicht besessenes Selbst] .

Wenn es abstürzt, wenn Sie [nicht besessenes Selbst] verwenden, ist Selbst wahrscheinlich irgendwann in diesem Abschluss gleich Null und Sie müssen wahrscheinlich stattdessen [schwaches Selbst] verwenden.

Schauen Sie sich die Beispiele zur Verwendung von starken , schwachen und nicht besessenen Verschlüssen an:

https://developer.apple.com/library/ios/documentation/swift/conceptual/swift_programming_language/AutomaticReferenceCounting.html


7
Warum nicht einfach schwach verwenden, auch wenn Selbst niemals Null sein kann, kein Schaden richtig angerichtet wird?
Boon

4
hi @Boon - das ist in der Tat die kritische Frage.
Fattie

[schwaches Selbst] => Wie kann selfNull sein, wenn ich in viewDidLoad () einen Abschluss verwende ?
Hassan Tareq

@ HassanTareq, ich denke, einige gute Beispiele werden in dem oben erwähnten Artikel erwähnt. Überprüfen Sie den Abschnitt "Auflösen starker Referenzzyklen für Verschlüsse", insb. Zitat: "Swift erfordert, dass Sie self.someProperty oder self.someMethod () schreiben (und nicht nur someProperty oder someMethod ()), wenn Sie innerhalb eines Abschlusses auf ein Mitglied von self verweisen. Dies hilft Ihnen, sich daran zu erinnern, dass es möglich ist, sich selbst zu erfassen Unfall." Auszug aus: Apple Inc. "Die Swift-Programmiersprache (Swift 4)." iBooks. itunes.apple.com/de/book/the-swift-programming-language-swift-4/… "
Nick Entin

1
@Boon Wenn Sie immer schwach verwenden, wird der Compiler vor der Verwendung erzwingen, nach optional zu suchen. Falls Sie diese Prüfung nicht durchgeführt haben, wird ein Fehler bei der Kompilierung angezeigt. Es gibt keinen anderen Schaden.
Vikas Mishra

5

Auszüge aus dem Link

Einige abschließende Punkte

  • Um festzustellen, ob Sie sich überhaupt um starke, schwache oder nicht besessene Sorgen machen müssen, fragen Sie: „Habe ich es mit Referenztypen zu tun?“. Wenn Sie mit Strukturen oder Aufzählungen arbeiten, verwaltet ARC den Speicher für diese Typen nicht und Sie müssen sich nicht einmal darum kümmern, für diese Konstanten oder Variablen schwach oder nicht besessen anzugeben.
  • Starke Referenzen sind in hierarchischen Beziehungen in Ordnung, in denen der Elternteil auf das Kind verweist, aber nicht umgekehrt. Tatsächlich sind starke Referenzen die meiste Zeit die am besten geeignete Art von Referenz.
  • Wenn zwei Instanzen optional miteinander verknüpft sind, stellen Sie sicher, dass eine dieser Instanzen einen schwachen Verweis auf die andere enthält.
  • Wenn zwei Instanzen so verknüpft sind, dass eine der Instanzen ohne die andere nicht existieren kann, muss die Instanz mit der obligatorischen Abhängigkeit einen nicht besessenen Verweis auf die andere Instanz enthalten.

1

Beides weakund unownedReferenzen wirken sich nicht auf die Referenzanzahl des Objekts aus. Eine schwache Referenz ist jedoch immer optional, dh sie kann gleich Null seinunowned Referenzen niemals null sein können, sodass sie niemals optional sind. Wenn Sie eine optionale Referenz verwenden, müssen Sie immer die Möglichkeit berücksichtigen, dass das Objekt Null ist. Im Falle einer nicht besessenen Referenz müssen Sie sicherstellen, dass das Objekt niemals Null ist. Die Verwendung eines nicht besessenen Verweises auf ein Null-Objekt ähnelt dem gewaltsamen Auspacken einer Option, die Null ist.

Es ist jedoch sicher, eine nicht im Besitz befindliche Referenz zu verwenden, bei der Sie sicher sind, dass die Lebensdauer des Objekts länger ist als die der Referenz. Ist dies nicht der Fall, ist es besser, stattdessen eine schwache Referenz zu verwenden.

Was den dritten Teil der Frage betrifft, denke ich nicht, dass eine nicht besessene Referenz einem baumelnden Zeiger ähnelt. Wenn wir über die Referenzanzahl sprechen, beziehen wir uns normalerweise auf die starke Referenzanzahl des Objekts. In ähnlicher Weise behält Swift die Anzahl der nicht besessenen Referenzen und die Anzahl der schwachen Referenzen für das Objekt bei (schwache Referenzpunkte verweisen eher auf eine sogenannte "Beistelltabelle" als auf das Objekt selbst). Wenn der starke Referenzzähler Null erreicht, wird das Objekt deinitialisiert, kann jedoch nicht freigegeben werden, wenn der nicht besessene Referenzzähler größer als Null ist.

Jetzt ist ein baumelnder Zeiger etwas, das auf einen Speicherplatz verweist, der bereits freigegeben wurde. Da der Speicher jedoch nur dann freigegeben werden kann, wenn ein nicht besessener Verweis auf das Objekt vorhanden ist, kann er keinen baumelnden Zeiger verursachen.

Es gibt viele Artikel, in denen die schnelle Speicherverwaltung ausführlicher behandelt wird. Hier ist einer.


0

Nicht besessene Referenzen sind eine Art schwache Referenz, die im Fall einer Beziehung mit gleicher Lebensdauer zwischen zwei Objekten verwendet wird, wenn ein Objekt immer nur einem anderen Objekt gehören sollte. Auf diese Weise können Sie eine unveränderliche Bindung zwischen einem Objekt und einer seiner Eigenschaften herstellen.

In dem Beispiel im schnellen WWDC-Zwischenvideo besitzt eine Person eine Kreditkarte, und eine Kreditkarte kann nur einen Inhaber haben. Auf der Kreditkarte sollte die Person keine optionale Eigenschaft sein, da Sie nicht möchten, dass eine Kreditkarte mit nur einem Eigentümer herumschwirrt. Sie können diesen Zyklus durchbrechen, indem Sie die Inhaber-Eigenschaft des Kredits zu einer schwachen Referenz machen. Dazu müssen Sie sie jedoch sowohl optional als auch variabel (im Gegensatz zur Konstanten) machen. Die nicht besessene Referenz in diesem Fall bedeutet, dass CreditCard zwar keine Beteiligung an einer Person besitzt, deren Leben jedoch davon abhängt.

class Person {
    var card: CreditCard?
}

class CreditCard {

    unowned let holder: Person

    init (holder: Person) {
        self.holder = holder
    }
}

Link zu wwdc Video oder Titel?
Osa

-2

Verwenden unownedSie diese Option, wenn Sie sicher sind, dass Sie selfniemals nilan dem Punkt sein können, auf den Sie zugreifenself an diesem Punkt .

Beispiel (Sie können das Ziel natürlich direkt von hinzufügen MyViewController, aber es ist wieder ein einfaches Beispiel):

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let myButton = MyButton { [unowned self] in
            print("At this point, self can NEVER be nil. You are safe to use unowned.")
            print("This is because myButton can not be referenced without/outside this instance (myViewController)")
        }
    }
}

class MyButton: UIButton {
    var clicked: (() -> ())

    init(clicked: (() -> ())) {
        self.clicked = clicked

        // We use constraints to layout the view. We don't explicitly set the frame.
        super.init(frame: .zero)

        addTarget(self, action: #selector(clicked), for: .touchUpInside)
    }

    @objc private func sendClosure() {
        clicked()
    }
}

Verwenden Sie diese Option, wenn an dem Punkt, auf den Sie zugreifen, weakeine Möglichkeit bestehtselfnilself .

Beispiel:

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        NetworkManager.sharedInstance.receivedData = { [weak self] (data) in
            print("Can you guarentee that self is always available when the network manager received data?")
            print("Nope, you can't. Network manager will be alive, regardless of this particular instance of MyViewController")
            print("You should use weak self here, since you are not sure if this instance is still alive for every")
            print("future callback of network manager")
        }
    }
}

class NetworkManager {

    static let sharedInstance = NetworkManager()

    var receivedData: ((Data) -> ())?

    private func process(_ data: Data) {
        // process the data...

        // ... eventually notify a possible listener.
        receivedData?(data)
    }
}

Nachteile von unowned:

  • Effizienter als schwach
  • Sie können (nun, Sie sind gezwungen) die Instanz als unveränderlich markieren (seit Swift 5.0 nicht mehr).
  • Zeigt dem Leser Ihres Codes an: Diese Instanz hat eine Beziehung zu X und kann ohne sie nicht leben, aber wenn X weg ist, bin ich auch weg.

Nachteile von weak:

  • Sicherer als nicht besessen (da es nicht abstürzen kann).
  • Kann eine Beziehung zu X herstellen, die in beide Richtungen geht, aber beide können ohne einander leben.

Wenn Sie sich nicht sicher sind, verwenden Sie weak. Warten Sie , ich meine, fragen Sie hier auf StackOverflow, was Sie in Ihrem Fall tun sollten! Die ständige Verwendung von Schwachstellen, wenn Sie dies nicht tun sollten, ist für Sie und den Leser Ihres Codes nur verwirrend.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.