J'ai bien peur que cette question soit assez basique, mais je pense qu'elle est pertinente pour beaucoup de programmeurs Objective-C qui se retrouvent dans des blocages.
Ce que j'ai entendu, c'est que puisque les blocs capturent les variables locales référencées en leur sein en tant que const
copies, l'utilisation self
dans un bloc peut entraîner un cycle de rétention, si ce bloc est copié. Donc, nous sommes censés utiliser __block
pour forcer le bloc à traiter directement au self
lieu de le copier.
__block typeof(self) bself = self;
[someObject messageWithBlock:^{ [bself doSomething]; }];
au lieu de juste
[someObject messageWithBlock:^{ [self doSomething]; }];
Ce que j'aimerais savoir est le suivant: si c'est vrai, y a-t-il un moyen d'éviter la laideur (en dehors de l'utilisation de GC)?
objective-c
memory-management
objective-c-blocks
Jonathan Sterling
la source
la source
self
mandatairesthis
juste pour inverser les choses. En JavaScript, j'appelle mesthis
fermeturesself
, donc c'est agréable et équilibré. :)Réponses:
Strictement parlant, le fait qu'il s'agisse d'une copie const n'a rien à voir avec ce problème. Les blocs conserveront toutes les valeurs obj-c capturées lors de leur création. Il se trouve que la solution de contournement pour le problème de copie de const est identique à la solution de contournement pour le problème de conservation; à savoir, en utilisant la
__block
classe de stockage pour la variable.Dans tous les cas, pour répondre à votre question, il n'y a pas de véritable alternative ici. Si vous concevez votre propre API basée sur des blocs, et que cela a du sens de le faire, vous pouvez faire passer la valeur de
self
in en tant qu'argument. Malheureusement, cela n'a pas de sens pour la plupart des API.Veuillez noter que le référencement d'un ivar pose exactement le même problème. Si vous avez besoin de référencer un ivar dans votre bloc, utilisez plutôt une propriété ou utilisez
bself->ivar
.Addendum: lors de la compilation en tant qu'ARC, les
__block
interruptions de cycle de conservation ne sont plus. Si vous compilez pour ARC, vous devez utiliser__weak
ou à la__unsafe_unretained
place.la source
__weak
va aussi. Si vous savez pertinemment que l'objet ne peut pas être hors de portée lorsque le bloc est appelé, alors il__unsafe_unretained
est un peu plus rapide, mais en général, cela ne fera aucune différence. Si vous l'utilisez__weak
, assurez-vous de le jeter dans une__strong
variable locale et de tester cela pour non-nil
avant de faire quoi que ce soit avec.__block
L'effet secondaire de ne pas retenir et de libérer était purement dû à l'incapacité de raisonner correctement à ce sujet. Avec ARC, le compilateur a acquis cette capacité, et donc__block
maintenant conserve et libère. Si vous devez éviter cela, vous devez utiliser__unsafe_unretained
, qui indique au compilateur de ne pas effectuer de retenues ou de versions sur la valeur de la variable.Utilisez simplement:
Pour plus d'informations: WWDC 2011 - Blocks and Grand Central Dispatch in Practice .
https://developer.apple.com/videos/wwdc/2011/?id=308
Remarque: si cela ne fonctionne pas, vous pouvez essayer
la source
Cela peut être évident, mais vous n'avez à faire le vilain
self
alias que lorsque vous savez que vous obtiendrez un cycle de rétention. Si le bloc est juste une chose ponctuelle, je pense que vous pouvez ignorer en toute sécurité la conservationself
. Le mauvais cas est lorsque vous avez le bloc comme interface de rappel, par exemple. Comme ici:Ici, l'API n'a pas beaucoup de sens, mais cela aurait du sens lors de la communication avec une superclasse, par exemple. Nous conservons le gestionnaire de tampon, le gestionnaire de tampon nous retient. Comparez avec quelque chose comme ceci:
Dans ces situations, je ne fais pas d'
self
alias. Vous obtenez un cycle de conservation, mais l'opération est de courte durée et le bloc finira par sortir de la mémoire, interrompant le cycle. Mais mon expérience avec les blocs est très petite et il se peut que l'self
aliasing soit une meilleure pratique à long terme.la source
copy
nonretain
. S'ils sont justeretain
, il n'y a aucune garantie qu'ils seront déplacés hors de la pile, ce qui signifie que lorsque vous allez l'exécuter, il ne sera plus là. (et la copie et le bloc déjà copié sont optimisés pour une conservation)retain
phase il y a quelque temps et j'ai rapidement réalisé ce que vous dites :) Merci!retain
est complètement ignoré pour les blocs (sauf s'ils ont déjà quitté la pile aveccopy
).Publier une autre réponse parce que c'était un problème pour moi aussi. Au départ, je pensais que je devais utiliser blockSelf partout où il y avait une auto-référence à l'intérieur d'un bloc. Ce n'est pas le cas, ce n'est que lorsque l'objet lui-même contient un bloc. Et en fait, si vous utilisez blockSelf dans ces cas, l'objet peut être libéré avant que vous ne récupériez le résultat du bloc, puis il plantera lorsqu'il essaiera de l'appeler, donc clairement vous voulez que le soi soit conservé jusqu'à la réponse revient.
Le premier cas montre quand un cycle de rétention se produira car il contient un bloc référencé dans le bloc:
Vous n'avez pas besoin de blockSelf dans le second cas car l'objet appelant ne contient pas de bloc qui provoquera un cycle de rétention lorsque vous référencez self:
la source
self
peut pas due aux personnes de plus-appliquer ce correctif. Ceci est un bon exemple pour éviter les cycles de rétention dans le code non ARC, merci pour la publication.Rappelez-vous également que des cycles de rétention peuvent se produire si votre bloc se réfère à un autre objet qui retient ensuite
self
.Je ne suis pas sûr que Garbage Collection puisse aider dans ces cycles de conservation. Si l'objet conservant le bloc (que j'appellerai l'objet serveur) survit
self
(l'objet client), la référence à l'self
intérieur du bloc ne sera pas considérée comme cyclique jusqu'à ce que l'objet de rétention lui-même soit libéré. Si l'objet serveur survit de loin à ses clients, vous pouvez avoir une fuite de mémoire importante.Puisqu'il n'y a pas de solutions propres, je recommanderais les solutions de contournement suivantes. N'hésitez pas à en choisir un ou plusieurs pour résoudre votre problème.
doSomethingAndWhenDoneExecuteThisBlock:
, et non des méthodes commesetNotificationHandlerBlock:
. Les blocs utilisés pour l'achèvement ont une fin de vie définie et doivent être libérés par les objets serveur après leur évaluation. Cela empêche le cycle de rétention de vivre trop longtemps même s'il se produit.dealloc
au lieu derelease
.Si vous écrivez un objet serveur, ne prenez les arguments de bloc que pour terminer. N'acceptez pas les arguments de bloc pour les rappels, tels que
setEventHandlerBlock:
. Au lieu de cela, revenez au modèle de délégué classique: créez un protocole formel et publiez unesetEventDelegate:
méthode. Ne retenez pas le délégué. Si vous ne souhaitez même pas créer de protocole formel, acceptez un sélecteur comme rappel de délégué.Et enfin, ce modèle devrait sonner des alarmes:
Si vous essayez de décrocher des blocs qui peuvent faire référence
self
de l'intérieurdealloc
, vous avez déjà des problèmes.dealloc
peut ne jamais être appelé en raison du cycle de rétention causé par les références dans le bloc, ce qui signifie que votre objet va simplement fuir jusqu'à ce que l'objet serveur soit désalloué.la source
__weak
correctement.__block __unsafe_unretained
les modificateurs suggérés dans l'article de Kevin peuvent provoquer l'exception d'accès incorrect en cas de blocage exécuté dans un thread différent. Il est préférable d'utiliser uniquement le modificateur __block pour la variable temporaire et de le rendre nul après l'utilisation.la source
Vous pouvez utiliser la bibliothèque libextobjc. Il est assez populaire, il est utilisé dans ReactiveCocoa par exemple. https://github.com/jspahrsummers/libextobjc
Il fournit 2 macros @weakify et @strongify, vous pouvez donc avoir:
Cela empêche une référence forte directe afin de ne pas entrer dans un cycle de rétention de soi. Et aussi, cela empêche le soi de devenir nul à mi-chemin, mais décrémente toujours correctement le nombre de rétention. Plus d'informations sur ce lien: http://aceontech.com/objc/ios/2014/01/10/weakify-a-more-elegant-solution-to-weakself.html
la source
Que dis-tu de ça?
Je ne reçois plus l'avertissement du compilateur.
la source
Bloc: un cycle de rétention se produira car il contient un bloc référencé dans le bloc; Si vous effectuez la copie de bloc et utilisez une variable membre, self sera conservé.
la source