Denken Sie auch daran, dass Aufbewahrungszyklen auftreten können, wenn Ihr Block auf ein anderes Objekt verweist, das dann beibehalten wird self
.
Ich bin nicht sicher, ob Garbage Collection bei diesen Aufbewahrungszyklen helfen kann. Wenn das Objekt, das den Block beibehält self
(das ich das Serverobjekt nenne), überlebt (das Client-Objekt), wird der Verweis auf das self
Innere des Blocks erst dann als zyklisch betrachtet, wenn das Aufbewahrungsobjekt selbst freigegeben wird. Wenn das Serverobjekt seine Clients weit überlebt, liegt möglicherweise ein erheblicher Speicherverlust vor.
Da es keine sauberen Lösungen gibt, würde ich die folgenden Problemumgehungen empfehlen. Sie können auch eine oder mehrere auswählen, um Ihr Problem zu beheben.
- Verwenden Sie Blöcke nur zur Vervollständigung und nicht für offene Ereignisse. Verwenden Sie beispielsweise Blöcke für Methoden wie
doSomethingAndWhenDoneExecuteThisBlock:
und nicht für Methoden wie setNotificationHandlerBlock:
. Für die Fertigstellung verwendete Blöcke haben ein bestimmtes Lebensende und sollten von Serverobjekten freigegeben werden, nachdem sie ausgewertet wurden. Dies verhindert, dass der Haltezyklus zu lange lebt, selbst wenn er auftritt.
- Machen Sie diesen von Ihnen beschriebenen schwachen Referenztanz.
- Stellen Sie eine Methode zum Bereinigen Ihres Objekts bereit, bevor es freigegeben wird, mit der das Objekt von Serverobjekten "getrennt" wird, die möglicherweise Verweise darauf enthalten. und rufen Sie diese Methode auf, bevor Sie release für das Objekt aufrufen. Diese Methode ist zwar vollkommen in Ordnung, wenn Ihr Objekt nur einen Client hat (oder in einem bestimmten Kontext ein Singleton ist), bricht jedoch zusammen, wenn es mehrere Clients hat. Sie besiegen hier im Grunde den Retain-Counting-Mechanismus. das ist vergleichbar mit anrufen
dealloc
statt release
.
Wenn Sie ein Serverobjekt schreiben, nehmen Sie Blockargumente nur zur Vervollständigung. Akzeptieren Sie keine Blockargumente für Rückrufe wie z setEventHandlerBlock:
. Greifen Sie stattdessen auf das klassische Delegatenmuster zurück: Erstellen Sie ein formales Protokoll und geben Sie eine setEventDelegate:
Methode bekannt. Behalten Sie den Delegierten nicht. Wenn Sie nicht einmal ein formelles Protokoll erstellen möchten, akzeptieren Sie einen Selektor als Delegatenrückruf.
Und schließlich sollte dieses Muster Alarm auslösen:
- (nichtiger) Dealloc {
[myServerObject releaseCallbackBlocksForObject: self];
...
}}
Wenn Sie versuchen, Blöcke zu lösen, auf die self
von innen verwiesen wird dealloc
, sind Sie bereits in Schwierigkeiten. dealloc
Möglicherweise wird es aufgrund des durch Verweise im Block verursachten Aufbewahrungszyklus niemals aufgerufen. Dies bedeutet, dass Ihr Objekt einfach leckt, bis die Zuordnung des Serverobjekts aufgehoben wird.
self
Proxies an,this
nur um die Dinge umzudrehen. In JavaScript nenne ich meinethis
Verschlüsseself
, damit es sich gut und ausgewogen anfühlt. :)