Warum erhalten Objective-C-Delegierte normalerweise die Zuweisung der Eigenschaft, anstatt sie beizubehalten?

176

Ich surfe durch den wunderbaren Blog von Scott Stevenson und versuche, ein grundlegendes Objective-C-Konzept zu verstehen, bei dem Delegierten die Eigenschaft "Zuweisen" gegenüber "Beibehalten" zugewiesen wird. Beachten Sie, dass beide in einer Müllsammelumgebung gleich sind. Ich beschäftige mich hauptsächlich mit einer nicht GC-basierten Umgebung (z. B. iPhone).

Direkt aus Scotts Blog:

"Das Schlüsselwort assign generiert einen Setter, der den Wert direkt der Instanzvariablen zuweist, anstatt ihn zu kopieren oder beizubehalten. Dies ist am besten für primitive Typen wie NSInteger und CGFloat oder für Objekte geeignet, die Sie nicht direkt besitzen, z. B. Delegaten."

Was bedeutet es, dass Sie das Delegatenobjekt nicht direkt besitzen? Normalerweise behalte ich meine Delegierten, denn wenn ich nicht möchte, dass sie in den Abgrund gehen, kümmert sich Retain für mich darum. Normalerweise abstrahiere ich UITableViewController von seiner jeweiligen Datenquelle und delegiere auch. Ich behalte auch dieses spezielle Objekt. Ich möchte sicherstellen, dass es nie verschwindet, damit mein UITableView immer einen Delegierten hat.

Kann jemand weiter erklären, wo / warum ich falsch liege, damit ich dieses gängige Paradigma in der Objective-C 2.0-Programmierung verstehen kann, die Zuweisungseigenschaft für Delegaten zu verwenden, anstatt sie beizubehalten?

Vielen Dank!

Plumenator
quelle
Mit "Delegierten" und ohne "iPhone" neu markiert.
Quinn Taylor
Warum wird der Delegat anstelle der Kopie zugewiesen (wie bei NSString?)
OMGPOP

Antworten:

175

Der Grund, warum Sie das Speichern von Delegierten vermeiden, besteht darin, dass Sie einen Aufbewahrungszyklus vermeiden müssen:

A erstellt B A setzt sich als B-Delegierter… A wird von seinem Eigentümer freigegeben

Wenn B A behalten hätte, würde A nicht freigegeben werden, da B A besitzt, so dass der Dealloc von A niemals aufgerufen würde, was dazu führen würde, dass sowohl A als auch B auslaufen.

Sie sollten sich keine Sorgen machen, dass A weggeht, weil es B besitzt und es somit im Dealloc loswird.

Andrew Pouliot
quelle
Ich stimme nicht zu, Mike. Ich habe gerade ein Problem gefunden, bei dem ein Modal einen Delegaten hat, der das Modal abweist. Wenn ich jedoch eine Speicherwarnung im Modal ausführe, wird der Delegat freigegeben. Wenn ich dann mein Modal entlasse, ist der Delegierte gleich Null. Absturz.
Paul Shapiro
Ok, ich bin anderer Meinung, aber Sie haben Recht, dass es ein Designfehler ist. Ich fand heraus, dass ich meinen Entlassungsruf über eine Kinderklasse des echten Entlassers weiterleitete. Diese Kinderklasse wurde freigelassen und konnte nicht an den Container-Delegierten des Modals weitergeleitet werden. Ich habe es geändert, um einen Zeiger an den endgültigen Delegierten weiterzugeben, und dieser wurde bei der Speicherwarnung nicht freigegeben, und alles ist gut.
Paul Shapiro
2
Ihr Code sollte niemals so geschrieben werden, dass ein Null-Delegat zum Absturz führt. Nur das besitzende Objekt sollte eine besitzende Referenz haben. Bei einer Freigabe müssen die Delegaten der eigenen Objekte auf Null gesetzt werden, bevor sie freigegeben werden. Dann werden alle an den Null-Delegaten gesendeten Nachrichten einfach ignoriert. Das Übergeben eines Nullobjekts in einer Nachricht kann jedoch zum Absturz führen. Stellen Sie nur sicher, dass Sie nicht so mit Delegierten umgehen.
David Gish
Warten Sie - ist das nicht was weak? Die Frage ist, warum assignstatt verwenden weak?
Wcochran
3
@wcochran: Nein, diese Frage ist, warum assignstatt verwenden retain. Die Frage ist älter als ARC; weakund strong(letzteres ist synonym mit retain) existierte nicht, bis ARC eingeführt wurde. Sie sollten Ihre Frage zu weakvs. assignseparat stellen.
Peter Hosey
44

Weil das Objekt, das die Delegatennachrichten sendet, den Delegaten nicht besitzt.

Oft ist es umgekehrt, als wenn sich ein Controller als Delegat einer Ansicht oder eines Fensters festlegt: Der Controller besitzt die Ansicht / das Fenster. Wenn also die Ansicht / das Fenster ihren Delegaten besitzt, besitzen sich beide Objekte gegenseitig. Dies ist natürlich ein Haltezyklus, ähnlich einem Leck mit der gleichen Konsequenz (Objekte, die tot sein sollten, bleiben am Leben).

In anderen Fällen handelt es sich bei den Objekten um Peers: Keiner besitzt den anderen, wahrscheinlich weil beide demselben dritten Objekt gehören.

In beiden Fällen sollte das Objekt mit dem Delegaten seinen Delegaten nicht behalten.

(Es gibt übrigens mindestens eine Ausnahme. Ich erinnere mich nicht, was es war, und ich glaube nicht, dass es einen guten Grund dafür gab.)


Nachtrag (hinzugefügt am 19.05.2012): Unter ARC sollten Sie weakanstelle von verwenden assign. Schwache Referenzen werden nilautomatisch eingestellt, wenn das Objekt stirbt, wodurch die Möglichkeit ausgeschlossen wird, dass das delegierende Objekt am Ende Nachrichten an den toten Delegierten sendet.

Wenn Sie sich aus irgendeinem Grund von ARC fernhalten, ändern Sie zumindest assignEigenschaften, die auf Objekte verweisen unsafe_unretained, wodurch deutlich wird, dass dies ein nicht beibehaltener, aber nicht auf Null setzender Verweis auf ein Objekt ist.

assign bleibt für Nicht-Objektwerte sowohl unter ARC als auch unter MRC geeignet.

Peter Hosey
quelle
13
NSURLConnectionbehält seinen Delegierten.
Ja, verwenden weak, aber das beantwortet nicht die ursprüngliche Frage: Warum verwendet Apple assignanstelle von weak?
Wcochran
@wcochran: Die ursprüngliche Frage lautete: "Warum werden delegierte Eigenschaften angegeben, assignanstatt sie beizubehalten ? " weakexistierte nicht, als es gefragt wurde. Ihre Frage ist eine andere, die Sie separat stellen sollten. Ich würde gerne antworten.
Peter Hosey
@wcochran und Peter, wurde diese Frage woanders gestellt?
Herr Rogers
17

Beachten Sie, dass es bei einem zugewiesenen Delegaten sehr wichtig ist, diesen Delegatenwert immer auf Null zu setzen, wenn die Zuordnung des Objekts aufgehoben wird. Daher sollte ein Objekt immer darauf achten, keine Delegatenreferenzen in Dealloc auszuschließen, wenn dies nicht der Fall ist anderswo gemacht.

Kendall Helmstetter Gelner
quelle
"Beachten Sie, dass es bei einem zugewiesenen Delegaten sehr wichtig ist, diesen Delegatenwert immer auf Null zu setzen, wenn die Zuordnung des Objekts aufgehoben wird." Warum?
Peter Hosey
2
Da jede verbleibende Referenzmenge ungültig wird, nachdem die Zuordnung eines Objekts aufgehoben wurde (auf einen Speicher zeigt, der nicht mehr der erwarteten Objektart zugeordnet ist) - und daher einen Absturz verursacht, wenn Sie versuchen, es zu verwenden. Ein Zeichen dafür im Debugger ist, wenn der Debugger behauptet, eine Variable habe einen Typ, der völlig falsch zu sein scheint, als was die Variable tatsächlich deklariert ist.
Kendall Helmstetter Gelner
1
Dies ist nur erforderlich, wenn das Objekt, dessen Delegierter Sie sind, von einer anderen Quelle wie einem Timer oder einem anderen asynchronen Rückruf beibehalten wird. Andernfalls wird die Zuordnung aufgehoben, nachdem Sie es freigegeben haben, und es wird nicht versucht, Delegatenmethoden aufzurufen.
Andrew Pouliot
@ Andrew: Das stimmt, aber wenn Sie es immer zur Gewohnheit machen, keine Delegierten auszuschließen, werden Sie nicht vergessen, wann es darauf ankommt, oder wenn Sie versehentlich ein gehaltenes Objekt überbewahren und es trotzdem in der Nähe bleibt. Wenn Sie den Delegaten nicht ausschließen, ist das Ergebnis nur ein Leck anstelle eines Lecks, gefolgt von einem Absturz.
Kendall Helmstetter Gelner
1

Einer der Gründe dafür ist die Vermeidung von Haltezyklen. Nur um das Szenario zu vermeiden, in dem A und B beide Objekte aufeinander verweisen und keines von ihnen aus dem Speicher freigegeben wird.

Die akute Zuweisung eignet sich am besten für primitive Typen wie NSInteger und CGFloat oder für Objekte, die Sie nicht direkt besitzen, z. B. Delegaten.

Puneet Sharma
quelle
Das ist eher aus dem Zitat des OP bzw. der akzeptierten Antwort kopiert, nicht wahr?
Dakab