Ich fürchte, diese Frage ist ziemlich einfach, aber ich denke, sie ist für viele Objective-C-Programmierer relevant, die in Blöcke geraten.
Was ich gehört habe ist, dass, da Blöcke lokale Variablen erfassen, auf die in ihnen als const
Kopien verwiesen wird , die Verwendung self
innerhalb eines Blocks zu einem Aufbewahrungszyklus führen kann, sollte dieser Block kopiert werden. Wir sollten also __block
den Block zwingen, direkt damit umzugehen, self
anstatt ihn kopieren zu lassen.
__block typeof(self) bself = self;
[someObject messageWithBlock:^{ [bself doSomething]; }];
statt nur
[someObject messageWithBlock:^{ [self doSomething]; }];
Was ich gerne wissen würde, ist Folgendes: Wenn dies zutrifft, gibt es eine Möglichkeit, die Hässlichkeit zu vermeiden (abgesehen von der Verwendung von GC)?
objective-c
memory-management
objective-c-blocks
Jonathan Sterling
quelle
quelle
self
Proxies an,this
nur um die Dinge umzudrehen. In JavaScript nenne ich meinethis
Verschlüsseself
, damit es sich gut und ausgewogen anfühlt. :)Antworten:
Genau genommen hat die Tatsache, dass es sich um eine konstante Kopie handelt, nichts mit diesem Problem zu tun. Blöcke behalten alle obj-c-Werte bei, die beim Erstellen erfasst werden. Es kommt einfach so vor, dass die Problemumgehung für das Problem mit der Konstantenkopie mit der Problemumgehung für das Problem mit dem Beibehalten identisch ist. nämlich unter Verwendung der
__block
Speicherklasse für die Variable.Um Ihre Frage zu beantworten, gibt es hier auf keinen Fall eine echte Alternative. Wenn Sie Ihre eigene blockbasierte API entwerfen und dies sinnvoll ist, wird dem Block möglicherweise der Wert von
self
in als Argument übergeben. Leider ist dies für die meisten APIs nicht sinnvoll.Bitte beachten Sie, dass das Referenzieren eines Ivar genau das gleiche Problem hat. Wenn Sie auf einen Ivar in Ihrem Block verweisen müssen, verwenden Sie stattdessen entweder eine Eigenschaft oder verwenden Sie
bself->ivar
.Nachtrag: Beim Kompilieren als ARC werden
__block
keine Haltezyklen mehr unterbrochen. Wenn Sie für ARC kompilieren, müssen Sie__weak
oder__unsafe_unretained
verwenden.quelle
__weak
ist auch in Ordnung. Wenn Sie sicher wissen, dass das Objekt beim Aufrufen des Blocks nicht außerhalb des Gültigkeitsbereichs liegen kann,__unsafe_unretained
ist es etwas schneller, aber im Allgemeinen macht das keinen Unterschied. Wenn Sie verwenden__weak
, stellen Sie sicher, dass Sie es in eine__strong
lokale Variable werfen und diese auf Nicht-Variablen testen,nil
bevor Sie etwas damit tun.__block
Der Nebeneffekt, nicht zu behalten und freizugeben, war allein auf die Unfähigkeit zurückzuführen, dies richtig zu begründen. Mit ARC hat der Compiler diese Fähigkeit erlangt und__block
behält und veröffentlicht sie nun. Wenn Sie dies vermeiden müssen, müssen Sie verwenden__unsafe_unretained
, wodurch der Compiler angewiesen wird, keine Aufbewahrungen oder Freigaben für den Wert in der Variablen durchzuführen.Benutz einfach:
Weitere Informationen: WWDC 2011 - Blöcke und Grand Central Dispatch in der Praxis .
https://developer.apple.com/videos/wwdc/2011/?id=308
Hinweis: Wenn dies nicht funktioniert, können Sie es versuchen
quelle
Dies mag offensichtlich sein, aber Sie müssen den hässlichen
self
Alias nur dann ausführen, wenn Sie wissen, dass Sie einen Aufbewahrungszyklus erhalten. Wenn der Block nur eine einmalige Sache ist, können Sie die Beibehaltung sicher ignorierenself
. Der schlimme Fall ist, wenn Sie den Block beispielsweise als Rückrufschnittstelle haben. Wie hier:Hier macht die API nicht viel Sinn, aber es wäre sinnvoll, wenn man zum Beispiel mit einer Superklasse kommuniziert. Wir behalten den Pufferhandler, der Pufferhandler behält uns. Vergleichen Sie mit so etwas:
In diesen Situationen mache ich kein
self
Aliasing. Sie erhalten zwar einen Aufbewahrungszyklus, der Vorgang ist jedoch nur von kurzer Dauer, und der Block wird schließlich nicht mehr gespeichert, wodurch der Zyklus unterbrochen wird. Aber meine Erfahrung mit Blöcken ist sehr gering und es könnte sein, dassself
Aliasing auf lange Sicht als Best Practice herauskommt.quelle
copy
werdenretain
. Wenn dies nurretain
der Fall ist, gibt es keine Garantie dafür, dass sie vom Stapel entfernt werden. Wenn Sie ihn ausführen, wird er nicht mehr vorhanden sein. (und das Kopieren und bereits kopierte Block wird auf eine Beibehaltung optimiert)retain
Phase durchlaufen und schnell gemerkt, was du sagst :) Danke!retain
dass Blöcke völlig ignoriert werden (es sei denn, sie haben sich bereits mit vom Stapel entferntcopy
).Eine weitere Antwort posten, da dies auch für mich ein Problem war. Ich dachte ursprünglich, ich müsste blockSelf überall dort verwenden, wo es eine Selbstreferenz innerhalb eines Blocks gibt. Dies ist nicht der Fall, sondern nur, wenn das Objekt selbst einen Block enthält. Wenn Sie in diesen Fällen blockSelf verwenden, kann das Objekt freigegeben werden, bevor Sie das Ergebnis aus dem Block zurückerhalten. Wenn es versucht, es aufzurufen, stürzt es ab, sodass Sie möchten, dass self bis zur Antwort erhalten bleibt kommt zurück.
Der erste Fall zeigt, wann ein Aufbewahrungszyklus auftritt, da er einen Block enthält, auf den im Block verwiesen wird:
Im zweiten Fall benötigen Sie blockSelf nicht, da das aufrufende Objekt keinen Block enthält, der einen Aufbewahrungszyklus verursacht, wenn Sie auf self verweisen:
quelle
self
möglicherweise nicht darauf zurückzuführen sind, dass Personen diesen Fix zu häufig anwenden. Dies ist ein gutes Beispiel für die Vermeidung von Aufbewahrungszyklen in Nicht-ARC-Code, danke für die Veröffentlichung.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 dasself
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.
doSomethingAndWhenDoneExecuteThisBlock:
und nicht für Methoden wiesetNotificationHandlerBlock:
. 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.dealloc
stattrelease
.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 einesetEventDelegate:
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:
Wenn Sie versuchen, Blöcke zu lösen, auf die
self
von innen verwiesen wirddealloc
, 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.quelle
__weak
angemessen verwenden.__block __unsafe_unretained
In Kevins Beitrag vorgeschlagene Modifikatoren können zu einer Ausnahme für fehlerhaften Zugriff führen, wenn ein Block in einem anderen Thread ausgeführt wird. Es ist besser, nur den Modifikator __block für die temporäre Variable zu verwenden und sie nach der Verwendung auf Null zu setzen.quelle
Sie können die Bibliothek libextobjc verwenden. Es ist sehr beliebt, es wird zum Beispiel in ReactiveCocoa verwendet. https://github.com/jspahrsummers/libextobjc
Es bietet 2 Makros @weakify und @strongify, so dass Sie haben können:
Dies verhindert eine direkte starke Referenz, so dass wir nicht in einen Retentionszyklus für uns selbst geraten. Außerdem verhindert es, dass das Selbst auf halbem Weg gleich Null wird, verringert jedoch die Anzahl der Retentionen ordnungsgemäß. Mehr unter diesem Link: http://aceontech.com/objc/ios/2014/01/10/weakify-a-more-elegant-solution-to-weakself.html
quelle
Wie wäre es damit?
Ich bekomme keine Compiler-Warnung mehr.
quelle
Block: Ein Aufbewahrungszyklus wird ausgeführt, da er einen Block enthält, auf den im Block verwiesen wird. Wenn Sie die Blockkopie erstellen und eine Elementvariable verwenden, bleibt self erhalten.
quelle