Réponse courte
Au lieu d'accéder self
directement, vous devez y accéder indirectement, à partir d'une référence qui ne sera pas conservée. Si vous n'utilisez pas le comptage de référence automatique (ARC) , vous pouvez le faire:
__block MyDataProcessor *dp = self;
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}
Le __block
mot-clé marque les variables qui peuvent être modifiées à l'intérieur du bloc (nous ne le faisons pas), mais elles ne sont pas non plus automatiquement conservées lorsque le bloc est conservé (sauf si vous utilisez ARC). Si vous faites cela, vous devez être sûr que rien d'autre ne va essayer d'exécuter le bloc après la libération de l'instance MyDataProcessor. (Compte tenu de la structure de votre code, cela ne devrait pas poser de problème.) En savoir plus__block
.
Si vous utilisez ARC , la sémantique des __block
modifications et la référence seront conservées, auquel cas vous devez la déclarer à la __weak
place.
Longue réponse
Disons que vous aviez un code comme celui-ci:
self.progressBlock = ^(CGFloat percentComplete) {
[self.delegate processingWithProgress:percentComplete];
}
Le problème ici est que self conserve une référence au bloc; pendant ce temps, le bloc doit conserver une référence à self afin de récupérer sa propriété de délégué et d'envoyer au délégué une méthode. Si tout le reste de votre application libère sa référence à cet objet, son nombre de retenues ne sera pas nul (car le bloc le pointe) et le bloc ne fait rien de mal (parce que l'objet le pointe) et ainsi la paire d'objets s'infiltrera dans le tas, occupant de la mémoire mais toujours inaccessible sans débogueur. Tragique, vraiment.
Ce cas pourrait être facilement résolu en faisant ceci à la place:
id progressDelegate = self.delegate;
self.progressBlock = ^(CGFloat percentComplete) {
[progressDelegate processingWithProgress:percentComplete];
}
Dans ce code, self retient le bloc, le bloc retient le délégué, et il n'y a pas de cycle (visible d'ici; le délégué peut conserver notre objet mais cela est hors de nos mains en ce moment). Ce code ne risque pas une fuite de la même manière, car la valeur de la propriété déléguée est capturée lors de la création du bloc, au lieu d'être recherchée lors de son exécution. Un effet secondaire est que, si vous modifiez le délégué après la création de ce bloc, le bloc enverra toujours des messages de mise à jour à l'ancien délégué. La probabilité que cela se produise ou non dépend de votre application.
Même si vous étiez cool avec ce comportement, vous ne pouvez toujours pas utiliser cette astuce dans votre cas:
self.dataProcessor.progress = ^(CGFloat percentComplete) {
[self.delegate myAPI:self isProcessingWithProgress:percentComplete];
};
Ici, vous passez self
directement au délégué dans l'appel de méthode, vous devez donc le faire quelque part. Si vous avez le contrôle sur la définition du type de bloc, la meilleure chose serait de passer le délégué dans le bloc en tant que paramètre:
self.dataProcessor.progress = ^(MyDataProcessor *dp, CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
};
Cette solution évite le cycle de conservation et appelle toujours le délégué actuel.
Si vous ne pouvez pas modifier le bloc, vous pouvez y faire face . La raison pour laquelle un cycle de rétention est un avertissement, et non une erreur, est qu'il n'épelle pas nécessairement le destin de votre application. Si MyDataProcessor
est capable de libérer les blocs lorsque l'opération est terminée, avant que son parent n'essaye de le libérer, le cycle sera interrompu et tout sera nettoyé correctement. Si vous pouvez en être sûr, la bonne chose à faire serait d'utiliser un #pragma
pour supprimer les avertissements pour ce bloc de code. (Ou utilisez un indicateur de compilation par fichier. Mais ne désactivez pas l'avertissement pour l'ensemble du projet.)
Vous pouvez également chercher à utiliser une astuce similaire ci-dessus, déclarer une référence faible ou non retenue et l'utiliser dans le bloc. Par exemple:
__weak MyDataProcessor *dp = self; // OK for iOS 5 only
__unsafe_unretained MyDataProcessor *dp = self; // OK for iOS 4.x and up
__block MyDataProcessor *dp = self; // OK if you aren't using ARC
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}
Les trois éléments ci-dessus vous donneront une référence sans conserver le résultat, bien qu'ils se comportent tous un peu différemment: __weak
tentera de mettre à zéro la référence lorsque l'objet est relâché; __unsafe_unretained
vous laissera un pointeur invalide; __block
ajoutera en fait un autre niveau d'indirection et vous permettra de modifier la valeur de la référence depuis l'intérieur du bloc (non pertinent dans ce cas, car il dp
n'est utilisé nulle part ailleurs).
Le meilleur dépendra du code que vous pouvez modifier et de ce que vous ne pouvez pas. Mais j'espère que cela vous a donné quelques idées sur la façon de procéder.
dp
sera publié (par exemple, s'il s'agissait d'un contrôleur de vue et qu'il a été déclenché), la ligne[dp.delegate ...
provoquera EXC_BADACCESS?strong
ouweak
?@weakify(..)
et@strongify(...)
qui vous permet d'utiliserself
en bloc de manière non conservante.Il existe également la possibilité de supprimer l'avertissement lorsque vous êtes certain que le cycle sera rompu à l'avenir:
De cette façon, vous n'avez pas à faire le tour avec
__weak
, l'self
alias et le préfixe ivar explicite.la source
__weak id weakSelf = self;
comportement est fondamentalement différent de la suppression de l'avertissement. La question a commencé par "... si vous êtes certain que le cycle de rétention sera rompu"[array addObject:weakObject];
si le faible objet a été libéré, cela provoque un crash. De toute évidence, cela n'est pas préféré à un cycle de rétention. Vous devez comprendre si votre bloc vit réellement assez longtemps pour justifier sa fragilisation, et aussi si vous voulez que l'action dans le bloc dépende de la validité de l'objet faible.Pour une solution commune, je les ai définis dans l'en-tête de précompilation. Évite la capture et permet toujours l'aide du compilateur en évitant d'utiliser
id
Ensuite, dans le code, vous pouvez faire:
la source
self
intérieur de votre bloc @weakify (self); bloc id = ^ {@strongify (self); [self.delegate myAPIDidFinish: self]; };Je crois que la solution sans ARC fonctionne également avec ARC, en utilisant le
__block
mot - clé:EDIT: Selon les notes de mise à jour de la transition vers ARC , un objet déclaré avec
__block
stockage est toujours conservé. Utilisez__weak
(préféré) ou__unsafe_unretained
(pour une compatibilité ascendante).la source
__block
mot clé évitait de conserver son référent. Merci! J'ai mis à jour ma réponse monolithique. :-)Combinant quelques autres réponses, voici ce que j'utilise maintenant pour un soi faible typé à utiliser en blocs:
J'ai défini cela comme un extrait de code XCode avec un préfixe de complétion "welf" dans les méthodes / fonctions, qui apparaît après avoir tapé uniquement "nous".
la source
warning => "la capture de soi à l'intérieur du bloc est susceptible d'entraîner un cycle de rétention"
lorsque vous référez vous-même ou sa propriété à l'intérieur d'un bloc qui est fortement retenu par vous-même, il apparaît ci-dessus.
donc pour l'éviter, nous devons en faire une semaine de référence
donc au lieu d'utiliser
nous devrions utiliser
Remarque: le cycle de rétention se produit généralement lorsque certains comment deux objets se référant l'un à l'autre par lesquels les deux ont le nombre de références = 1 et leur méthode delloc n'est jamais appelé.
la source
La nouvelle façon de procéder consiste à utiliser @weakify et @strongify marco
Plus d'informations sur @Weakify @Strongify Marco
la source
Si vous êtes sûr que votre code ne créera pas de cycle de rétention ou que le cycle sera interrompu plus tard, la façon la plus simple de désactiver l'avertissement est la suivante:
La raison pour laquelle cela fonctionne est que, bien que l'accès aux points des propriétés soit pris en compte par l'analyse de Xcode, et donc
est considéré comme ayant une retenue par x de y (sur le côté gauche de l'affectation) et par y de x (sur le côté droit), les appels de méthode ne sont pas soumis à la même analyse, même lorsqu'ils sont des appels de méthode d'accès à la propriété qui sont équivalents à dot-access, même lorsque ces méthodes d'accès aux propriétés sont générées par le compilateur, donc dans
seul le côté droit est considéré comme créant une rétention (par y de x), et aucun avertissement de cycle de rétention n'est généré.
la source