La semaine dernière il y avait un peu de Twitter en combat dans la communauté iOS sur la «bonne» façon de libérer votre variables d'instance dans dealloc. Je pense que Rob réellement démarré, pour être honnête, mais je ne devrais probablement pas être en avoir parlé.
Fondamentalement, plusieurs développeurs ont été affirmant qu'il n'y a jamais une raison pour définir une variable d'instance à zéro en dealloc, tandis que d'autres soutenaient que vous devriez toujours le faire.
Pour moi, il ne semble pas être un gagnant clair et convaincant entre les deux approches. J'ai utilisé aussi bien dans ma carrière. Cependant, puisque nous sommes en train d'essayer de décider de l'approche à utiliser dans la prochaine édition de début de développement iPhone, j'ai tendu la main à Apple Developer Tools évangéliste, Michael Jurewitz, Pour voir s'il y avait un fonctionnaire ou d'approche recommandée pour la manipulation des variables d'instance dans le dealloc.
Autre que le fait que vous devriez jamais, jamais l'utilisation des mutateurs dans dealloc (ou init, d'ailleurs), Apple n'a pas une recommandation officielle sur le sujet.
Cependant, Michael et Matt Drance of Bookhouse Software et un évangéliste lui-même ancien, avait discuté de cette question en profondeur la semaine dernière. Ils ont gentiment partagé leurs conclusions avec moi et m'a dit que tout allait bien pour moi d'en faire un blog. Alors, c'est ici. Heureusement, j'ai capturé tout correctement.
Dans un environnement multi-thread, cependant, il ya une possibilité très réelle que le pointeur sera accessible entre le moment où elle est libérée et le temps que son objet est fait d'être libéré. De manière générale, ce n'est que va se produire si vous avez un bug ailleurs dans votre code, mais avouons-le, vous pouvez très bien. Quiconque codes sur l'hypothèse que l'ensemble de leur code est parfait est la mendicité pour une SmackDown, et Xcode juste attend son heure d'attente pour l'occasion.
Dans la première approche, votre demande sera généralement collision avec une EXC_BAD_ACCESS, mais vous pouvez aussi retrouver avec toute forme de comportement étrange (que nous appelons un «heisenbug") si l'objet est libérée libérée et sa mémoire est ensuite réutilisée pour un autre objet détenue par votre application. Dans ces cas, vous pouvez obtenir une exception de sélection n'est pas reconnu lorsque le message est envoyé à l'objet désallouée, ou vous pouvez simplement obtenir un comportement inattendu de la même méthode appelée sur le mauvais objet.
Dans l'autre approche, votre application va tranquillement envoyer un message à nil et aller sur son petit bonhomme de chemin, généralement sans un accident ou tout autre problème immédiatement identifiable.
La première approche est réellement bon quand vous êtes en développement, le débogage, et de faire les tests unitaires, car il rend plus facile de trouver votre code problématique. D'autre part, cette approche est vraiment, vraiment mauvais dans une application d'expédition parce que vous ne voulez vraiment pas s'écraser sur vos utilisateurs si vous ne pouvez l'éviter.
La dernière approche, à l'inverse, peuvent se cacher des bugs lors du développement, mais gère ces bugs avec plus de grâce quand ils se produisent, et vous êtes beaucoup moins susceptibles d'avoir votre application monter dans une grosse boule de feu devant vos utilisateurs.
Si vous voulez que votre application crash quand un pointeur publié est accessible durant le développement et le débogage, la solution est d'utiliser l'approche traditionnelle dans votre configuration de débogage. Si vous voulez que votre application dégradé pour vos utilisateurs, la solution est d'utiliser la nouvelle approche dans votre communiqué et ad hoc configurations.
Une mise en œuvre quelque peu pédant de cette approche serait la suivante:
Ce code suppose que votre configuration de débogage a une définition du précompilateur DEBUG, ce qui vous avez généralement à ajouter à votre projet Xcode - la plupart des modèles de projet Xcode ne le donnez pas à votre place. Il ya plusieurs façons que vous pouvez l'ajouter, mais je généralement il suffit d'utiliser le Macros préprocesseur mise en configuration de construction du projet:

Bien que le code ne-dessus, en effet, de nous donner le meilleur de deux mondes - un crash lors du développement et de débogage et gracieux dégradantes pour les clients - c'est moins doubler la quantité de code que nous avons à écrire dans chaque classe. Nous pouvons faire mieux que cela, cependant. Que diriez-vous d'une magie petite macro? Si l'on ajoute la macro suivante pour notre projet pch.:
Nous pouvons ensuite utiliser cette macro dans dealloc, et notre meilleur-de-deux-mondes code devient beaucoup plus court et plus lisible:
Une fois que vous avez la macro dans votre projet, cette option n'est en fait pas plus de travail ou en tapant à l'une des méthodes dealloc autres.
Mais, vous savez quoi? Si vous voulez continuer à faire les choses comme vous l'avez toujours fait, c'est vraiment bien, quelle que soit la façon dont vous le faites. Si vous êtes cohérent dans votre utilisation et sont conscients des compromis, il n'ya vraiment aucune raison impérieuse d'utiliser l'un sur l'autre en dehors de préférence personnelle.
Donc, en d'autres termes, c'est une sorte de bêtise pour nous tous de faire valoir plus, surtout quand il ya déjà la politique, les religions, et des sports pour remplir ce rôle.
S'il est vrai que dans Java et autres langages de la collecte des ordures, du bruit résiduel à un pointeur que vous en avez terminé avec l'aide garbage collector savons que vous avez terminé avec cet objet, donc ce n'est pas tout à fait improbable que le garbage collector Objective-C fait la même chose, cependant aucun bénéfice pour les variables d'instance nil'ing fois que nous aurons collecte des ordures dans iOS serait au mieux marginal. Je n'ai pas été capable de trouver quelque chose d'autorité qui soutient cette revendication, mais même si c'est 100% vrai, il semble probable que, lorsque l'objet propriétaire est désalloué quelques instructions plus tard, le garbage collector se rendra compte que l'objet se fait avec désallouée tout ce qu'il a été utilisé.
Si il ya un avantage dans ce scénario, qui semble improbable, il semble que ce serait une telle différence minuscule qui il ne serait même judicieux d'en tenir compte dans votre décision. Cela étant dit, je n'ai aucune preuve solide d'une manière ou l'autre sur cette question et souhaiterait être éclairé.
Après avoir écrit cela, Michael Jurewitz m'a cinglé à confirmé qu'il n'y a absolument aucun avantage en termes de GC pour la libération de dealloc, mais il en est autrement une bonne idée de pointeurs nuls, vous en avez terminé avec la CG..
Merci à @ et @ borkware eridius pour souligner certaines des questions
Update: Ne manquez pas excellente Daniel Jalkut de response.
Fondamentalement, plusieurs développeurs ont été affirmant qu'il n'y a jamais une raison pour définir une variable d'instance à zéro en dealloc, tandis que d'autres soutenaient que vous devriez toujours le faire.
Pour moi, il ne semble pas être un gagnant clair et convaincant entre les deux approches. J'ai utilisé aussi bien dans ma carrière. Cependant, puisque nous sommes en train d'essayer de décider de l'approche à utiliser dans la prochaine édition de début de développement iPhone, j'ai tendu la main à Apple Developer Tools évangéliste, Michael Jurewitz, Pour voir s'il y avait un fonctionnaire ou d'approche recommandée pour la manipulation des variables d'instance dans le dealloc.
Autre que le fait que vous devriez jamais, jamais l'utilisation des mutateurs dans dealloc (ou init, d'ailleurs), Apple n'a pas une recommandation officielle sur le sujet.
Cependant, Michael et Matt Drance of Bookhouse Software et un évangéliste lui-même ancien, avait discuté de cette question en profondeur la semaine dernière. Ils ont gentiment partagé leurs conclusions avec moi et m'a dit que tout allait bien pour moi d'en faire un blog. Alors, c'est ici. Heureusement, j'ai capturé tout correctement.
Les deux grandes approches
Juste pour vous assurer que nous sommes tous sur la même page, regardons les deux approches qui ont constitué les deux côtés différents de l'argument de la semaine dernière.Il suffit de relâcher
L'approche la plus traditionnelle est tout simplement de libérer votre variables d'instance et les laisser pointant vers l'objet libéré (et potentiellement libérée), comme ceci:- (void)dealloc { [Sneezy release]; [Sleepy release]; [Dopey release]; [Doc release]; [Happy release]; [Bashful release]; [Grumpy release]; [super dealloc]; } Dans cette approche, chacun des pointeurs sera pointant vers un objet potentialy invalide pour une période de temps très court - jusqu'à ce que le retour de la méthode - à quel point la variable d'instance va disparaître avec son objet propriétaire. Dans une application mono-thread (le pointeur devra être consulté par quelque chose déclenchée par le code soit dans la mise en œuvre de cet objet d'dealloc ou dans le dealloc de l'un de ses super, il ya très peu de chances que les variables d'instance sera utilisé avant de se rendre loin, ce qui est probablement ce qui a conduit à la proclamation faite par plusieurs «il n'existe pas de valeur dans la mise en variables d'instance à néant» dans dealloc. Dans un environnement multi-thread, cependant, il ya une possibilité très réelle que le pointeur sera accessible entre le moment où elle est libérée et le temps que son objet est fait d'être libéré. De manière générale, ce n'est que va se produire si vous avez un bug ailleurs dans votre code, mais avouons-le, vous pouvez très bien. Quiconque codes sur l'hypothèse que l'ensemble de leur code est parfait est la mendicité pour une SmackDown, et Xcode juste attend son heure d'attente pour l'occasion.
Libération et nulle
Dans les dernières années, une autre approche à dealloc est devenue plus courante. Dans cette approche, vous relâchez votre variable d'instance, puis immédiatement à les mettre à néant avant de libérer la variable d'instance suivante. Il est commun de mettre réellement la libération et l'assignation à zéro sur la même ligne séparés par une virgule plutôt que sur des lignes consécutives séparées par des virgules, mais c'est purement stylistique et n'a aucune incidence sur la façon dont le code est compilé. Voici ce que notre méthode dealloc précédente pourrait ressembler à l'aide de cette approche:- (void)dealloc { [sneezy release], sneezy = nil; [sleepy release], sleepy = nil; [dopey release], dopey = nil; [doc release], doc = nil; [happy release], happy = nil; [bashful release], bashful = nil; [grumpy release], grumpy = nil; [super dealloc]; } Dans ce cas, si certains morceau de code accède à un pointeur entre le moment où commence et dealloc l'objet est réellement libérée, elle échouera presque certainement parce gracieusement envoyer un message à nil est parfaitement correct en Objective-C. Toutefois, vous faites un petit peu de travail supplémentaire en assignant nulle à tout un tas de pointeurs qui vont s'en aller momentanément, et vous créez un petit peu de frappes au clavier pour vous-même dans chaque classe.Le Showdown
Donc, voici la vraie vérité de l'affaire: La grande majorité du temps, ça ne va pas faire de différence notable que ce soit. Si vous n'êtes pas accéder aux variables d'instance qui ont été libérés, il n'y a tout simplement pas y avoir de différence dans le comportement entre les deux approches. Si vous êtes, cependant, la question est: qu'est-ce que vous voulez arriver lorsque votre code est-ce que une mauvaise chose?Dans la première approche, votre demande sera généralement collision avec une EXC_BAD_ACCESS, mais vous pouvez aussi retrouver avec toute forme de comportement étrange (que nous appelons un «heisenbug") si l'objet est libérée libérée et sa mémoire est ensuite réutilisée pour un autre objet détenue par votre application. Dans ces cas, vous pouvez obtenir une exception de sélection n'est pas reconnu lorsque le message est envoyé à l'objet désallouée, ou vous pouvez simplement obtenir un comportement inattendu de la même méthode appelée sur le mauvais objet.
Dans l'autre approche, votre application va tranquillement envoyer un message à nil et aller sur son petit bonhomme de chemin, généralement sans un accident ou tout autre problème immédiatement identifiable.
La première approche est réellement bon quand vous êtes en développement, le débogage, et de faire les tests unitaires, car il rend plus facile de trouver votre code problématique. D'autre part, cette approche est vraiment, vraiment mauvais dans une application d'expédition parce que vous ne voulez vraiment pas s'écraser sur vos utilisateurs si vous ne pouvez l'éviter.
La dernière approche, à l'inverse, peuvent se cacher des bugs lors du développement, mais gère ces bugs avec plus de grâce quand ils se produisent, et vous êtes beaucoup moins susceptibles d'avoir votre application monter dans une grosse boule de feu devant vos utilisateurs.
Le gagnant?
Il n'est vraiment pas un gagnant claire, qui est probablement pourquoi Apple n'a pas une recommandation officielle ou de position. Lors de leur discussion, Matt et Michael venu avec "le meilleur des deux mondes" une solution, mais elle exige un peu juste de code supplémentaire dans l'une des approches communes.Si vous voulez que votre application crash quand un pointeur publié est accessible durant le développement et le débogage, la solution est d'utiliser l'approche traditionnelle dans votre configuration de débogage. Si vous voulez que votre application dégradé pour vos utilisateurs, la solution est d'utiliser la nouvelle approche dans votre communiqué et ad hoc configurations.
Une mise en œuvre quelque peu pédant de cette approche serait la suivante:
- (void)dealloc { #if DEBUG [Sneezy release]; [Sleepy release]; [Dopey release]; [Doc release]; [Happy release]; [Bashful release]; [Grumpy release]; [super dealloc]; #else [sneezy release], sneezy = nil; [sleepy release], sleepy = nil; [dopey release], dopey = nil; [doc release], doc = nil; [happy release], happy = nil; [bashful release], bashful = nil; [grumpy release], grumpy = nil; [super dealloc]; #endif } Ce code suppose que votre configuration de débogage a une définition du précompilateur DEBUG, ce qui vous avez généralement à ajouter à votre projet Xcode - la plupart des modèles de projet Xcode ne le donnez pas à votre place. Il ya plusieurs façons que vous pouvez l'ajouter, mais je généralement il suffit d'utiliser le Macros préprocesseur mise en configuration de construction du projet:
Bien que le code ne-dessus, en effet, de nous donner le meilleur de deux mondes - un crash lors du développement et de débogage et gracieux dégradantes pour les clients - c'est moins doubler la quantité de code que nous avons à écrire dans chaque classe. Nous pouvons faire mieux que cela, cependant. Que diriez-vous d'une magie petite macro? Si l'on ajoute la macro suivante pour notre projet pch.:
#if DEBUG #define MCRelease(x) [x release] #else #define MCRelease(x) [x release], x = nil #endifNous pouvons ensuite utiliser cette macro dans dealloc, et notre meilleur-de-deux-mondes code devient beaucoup plus court et plus lisible:
- (void)dealloc { MCRelease(sneezy); MCRelease(sleepy); MCRelease(dopey); MCRelease(doc); MCRelease(happy); MCRelease(bashful); MCRelease(grumpy); [super dealloc]; }Une fois que vous avez la macro dans votre projet, cette option n'est en fait pas plus de travail ou en tapant à l'une des méthodes dealloc autres.
Mais, vous savez quoi? Si vous voulez continuer à faire les choses comme vous l'avez toujours fait, c'est vraiment bien, quelle que soit la façon dont vous le faites. Si vous êtes cohérent dans votre utilisation et sont conscients des compromis, il n'ya vraiment aucune raison impérieuse d'utiliser l'un sur l'autre en dehors de préférence personnelle.
Donc, en d'autres termes, c'est une sorte de bêtise pour nous tous de faire valoir plus, surtout quand il ya déjà la politique, les religions, et des sports pour remplir ce rôle.
L'angle de collecte des ordures
Il ya un dernier point que je veux aborder. J'ai entendu à quelques reprises par différentes personnes que la mise en une variable d'instance à néant dans des actes dealloc comme un indice de la garbage collector lorsque vous utilisez l'option a permis-pas-besoin GC (lorsque l'option requise est utilisée, dealloc n'est même pas appelé, de finaliser l'est). Si cela était vrai, la compatibilité ascendante serait un autre argument possible pour préférer la nouvelle approche de dealloc sur l'approche traditionnelle.S'il est vrai que dans Java et autres langages de la collecte des ordures, du bruit résiduel à un pointeur que vous en avez terminé avec l'aide garbage collector savons que vous avez terminé avec cet objet, donc ce n'est pas tout à fait improbable que le garbage collector Objective-C fait la même chose, cependant aucun bénéfice pour les variables d'instance nil'ing fois que nous aurons collecte des ordures dans iOS serait au mieux marginal. Je n'ai pas été capable de trouver quelque chose d'autorité qui soutient cette revendication, mais même si c'est 100% vrai, il semble probable que, lorsque l'objet propriétaire est désalloué quelques instructions plus tard, le garbage collector se rendra compte que l'objet se fait avec désallouée tout ce qu'il a été utilisé.
Si il ya un avantage dans ce scénario, qui semble improbable, il semble que ce serait une telle différence minuscule qui il ne serait même judicieux d'en tenir compte dans votre décision. Cela étant dit, je n'ai aucune preuve solide d'une manière ou l'autre sur cette question et souhaiterait être éclairé.
Après avoir écrit cela, Michael Jurewitz m'a cinglé à confirmé qu'il n'y a absolument aucun avantage en termes de GC pour la libération de dealloc, mais il en est autrement une bonne idée de pointeurs nuls, vous en avez terminé avec la CG..
Merci à @ et @ borkware eridius pour souligner certaines des questions
Update: Ne manquez pas excellente Daniel Jalkut de response.
Aucun commentaire:
Enregistrer un commentaire