Réponse courte
Au lieu d'accéder self
directement, vous devriez accéder indirectement, à partir d'une référence qui ne sera pas conservée. Si vous n'êtes pas en utilisant Automatique de Comptage de Référence (ARC), vous pouvez faire ceci:
__block MyDataProcessor *dp = self;
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}
L' __block
mot-clé marques de variables qui peuvent être modifiées à l'intérieur du bloc (nous ne faisons pas que), mais aussi qu'ils ne sont pas retenus lorsque le bloc est conservé (sauf si vous utilisez un ARC). Si vous faites cela, vous devez être sûr que rien d'autre ne se passe à essayer d'exécuter le bloc après le MyDataProcessor instance est libéré. (Compte tenu de la structure de votre code, cela ne devrait pas être un problème). Lire plus à propos de __block
.
Si vous utilisez un ARC, la sémantique de l' __block
changements et la référence sera conservé, dans ce cas, vous devez le déclarer __weak
à la place.
Réponse longue
Disons que vous avez eu un code comme ceci:
self.progressBlock = ^(CGFloat percentComplete) {
[self.delegate processingWithProgress:percentComplete];
}
Le problème ici est que l'auto est maintenant une référence pour le bloc; pendant ce temps, le bloc doit conserver une référence à l'auto pour aller chercher son délégué propriété et envoyer le délégué d'une méthode. Si tout le reste dans votre application libère sa référence à cet objet, sa conserver le comte de ne pas être égale à zéro (parce que le bloc est le montrant du doigt) et le bloc n'est pas de faire quelque chose de mal (parce que l'objet est le montrant du doigt) et la paire d'objets de fuite dans le tas, d'occupation de la mémoire, mais à jamais inaccessible sans un 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, l'auto est en conservant le bloc, le bloc est de conserver le délégué, et il n'y a pas de cycles (visible à partir d'ici; le délégué peut conserver notre objet, mais c'est hors de nos mains en ce moment). Ce code ne sera pas le risque d'une fuite de la même manière, parce que la valeur du délégué de la propriété est capturée lorsque le bloc est créé, au lieu de regardé quand il s'exécute. Un effet secondaire est que, si vous modifiez le délégué après ce bloc est créé, le bloc va encore envoyer des messages de mise à jour de l'ancien délégué. Si cela est susceptible de se produire ou non dépend de votre application.
Même si vous avez été 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 êtes de passage self
directement au délégué à l'appel de la méthode, de sorte que vous devez 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 les conserver cycle et appelle toujours l'actuel délégué.
Si vous ne pouvez pas changer le bloc, vous pouvez traiter avec elle. La raison pour conserver un cycle est un avertissement, pas une erreur, c'est qu'ils n'ont pas nécessairement catastrophique pour votre application. Si MyDataProcessor
a la possibilité de libérer les blocs lorsque l'opération est terminée, avant que sa mère serait tenter de le libérer, le cycle sera brisé et tout sera nettoyé correctement. Si vous pouviez être sûr de cela, alors que la bonne chose à faire serait d'utiliser un #pragma
de supprimer les mises en garde pour ce bloc de code. (Ou utiliser un fichier par fichier compilateur drapeau. Mais ne pas désactiver l'avertissement pour l'ensemble du projet.)
Vous pouvez aussi rechercher dans l'aide d'une astuce similaire ci-dessus, la déclaration d'une référence de faiblesse ou de consignes et de l'aide que 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];
}
Tous les trois ci-dessus vous donnera une référence sans conserver le résultat, mais tous, ils se comportent un peu différemment: __weak
va essayer de zéro de référence lorsque l'objet est libéré, __unsafe_unretained
vous laisse avec un pointeur non valide; __block
sera effectivement ajouter un autre niveau d'indirection et vous permettent de modifier la valeur de référence à partir de l'intérieur du bloc (pas pertinent dans ce cas, depuis dp
n'est pas utilisé n'importe où ailleurs).
Ce qui est le mieux dépendra de ce code, vous êtes en mesure de changer et 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.