Quelle est la différence fondamentale entre Free et FreeAndNil ?
Est-ce que FreeAndNil = Free + Nil ?
Quand dois-je utiliser Free et quand dois-je utiliser FreeAndNil ?
Quelle est la différence fondamentale entre Free et FreeAndNil ?
Est-ce que FreeAndNil = Free + Nil ?
Quand dois-je utiliser Free et quand dois-je utiliser FreeAndNil ?
Voir
Et jetez un coup d'œil à la mise en œuvre :
procedure FreeAndNil(var Obj);
var
Temp: TObject;
begin
Temp := TObject(Obj);
Pointer(Obj) := nil;
Temp.Free;
end;
Considérons le code suivant :
procedure TForm1.FormCreate(Sender: TObject);
var
bm: TBitmap;
begin
bm := TBitmap.Create;
bm.LoadFromFile('C:\Users\Andreas Rejbrand\Documents\RAD Studio\6.0\Demos\DelphiWin32\VCLWin32\Football\up.bmp');
bm.Free;
if Assigned(bm) then
bm.SaveToFile('C:\Users\Andreas Rejbrand\Desktop\test.bmp')
else
ShowMessage('Cannot save! The bitmap does no longer exist!');
end;
Cela créera une erreur ou un bitmap invalide (vide) sur mon bureau, car j'essaie d'utiliser un objet qui a été libéré. Oui, même si bm
a été libéré, il est toujours "assigné", c'est-à-dire que bm
pointe toujours vers une adresse mémoire, même s'il n'y a rien (d'utilisable) à cet endroit. Pour surmonter ce problème, on peut définir bm := nil
à titre de garantie, alors assigned(bm)
retournera false, comme on le souhaite. Plus ou moins, FreeAndNil(bm)
est un raccourci pour bm.Free; bm := nil
. La première déclaration libère toute la mémoire (et les ressources du système d'exploitation, le temps CPU, etc. utilisés par l'objet), et bm := nil
définit le "pointeur". bm
à nil
de sorte que bm
ne pointe plus vers l'endroit où l'objet était, mais n'est plus. De cette façon, vous (et les routines comme assigned
) ne sera pas dupé en croyant qu'il existe toujours un objet bitmap.
Certains disent que vous devriez toujours utiliser FreeAndNil(foo)
plutôt que foo.Free
. Pourquoi pas ? L'instruction supplémentaire foo := nil
ne prendra probablement pas trop de nanosecondes pour s'exécuter, et en effet assigned(foo) = false
est une très belle propriété d'un objet libéré. Mais encore une fois, si vous savez ce que vous faites, et que vous savez que vous n'utiliserez jamais la fonction foo
après l'avoir libéré, alors vous pouvez vous contenter de foo.free
. Vraiment, certains diraient que dans de nombreux cas (mais pas tous), essayer d'utiliser une variable d'un objet libéré est un bug en soi. (Bien sûr, il y a des cas où vous le faites intentionnellement - vous avez un objet foo
qui est parfois assigné et parfois non).
"certains diront que dans de nombreux cas (mais pas tous), essayer d'utiliser un objet qui a été libéré est un bug en soi" - c'est toujours un bug, pas "parfois". Les variables et les objets sont deux choses différentes : les variables peuvent parfois se voir attribuer "nil", mais ce n'est pas un objet nil, c'est juste une variable non attribuée.
@Cosmin - pas tout à fait vrai. Les variables non assignées ont très peu de chances d'être NIL, à moins qu'il ne s'agisse de chaînes de caractères ou de références à des interfaces. D'un autre côté, les membres de classe non assignés sont certains d'être NIL à moins que vous ne surchargez délibérément l'implémentation de NewInstance.
J'irais même jusqu'à dire qu'un programmeur devrait rendre ses intentions claires en utilisant foo.Free quand foo n'est plus référencé, et FreeAndNil(foo) quand il l'est. N'utilisez pas FreeAndNil(foo) quand foo.Free ferait l'affaire, parce que cela suggère au lecteur que foo besoins doit être mis à zéro.
Basiquement, FreeAndNil met la référence à néant puis libère l'objet. Cela le marque comme non assigné. La seule raison pour laquelle vous devez utiliser FreeAndNil est que votre code va réutiliser la référence. Si vous êtes dans un destructeur ou un enfin bloquer, libérer des objets que vous ne toucherez plus jamais, il suffit d'utiliser Free.
Voir La gestion de la mémoire dans Delphi en toute simplicité pour un exemple de cas où je l'ai trouvé utile. Le commentaire de Mghie en bas de page vaut également la peine d'être lu.
En fait, il met la référence dans une var locale, nil la référence et ensuite libère l'objet en utilisant la var locale. FreeAndNil aurait dû être nommé NilAndFree... :)
Marjan, j'ai récemment eu un cas où cette dénomination m'aurait probablement fait gagner du temps dans l'identification d'un bogue dans une implémentation trop complexe de singleton qui impliquait l'accès à des variables de classe à partir d'un destructeur qui avait été auparavant FreeAndNil
'd... ;)
Je vais répondre d'une manière différente.
Peut-être, en remplissant votre référence d'objet avec nil
après avoir libéré votre objet n'est pas toujours une bonne idée.
De cette façon, vous n'avez pas de distinction entre une référence qui n'a jamais été utilisée (et donc qui est nil
), et une référence qui a été utilisée, mais qui ne devrait pas être utilisée à l'avenir.
Ainsi, en le remplissant avec un nombre magique (similaire à ce que le FastMM Le gestionnaire de mémoire peut faire avec le contenu des blocs de mémoire lorsque ces blocs sont libérés).
--jeroen
Je suis fortement en faveur de FreeAndNil mais votre idée de "nombre magique" est également séduisante.
Cette déclaration n'a de sens que si la logique de votre programme repose EXPLICITEMENT sur la logique "créer un objet une fois, le jeter et ne jamais le recréer". Ce qui est.... inhabituel.... Pouvez-vous donner un exemple où vous avez réellement utilisé cette technique ?
@Z80 par exemple lors du zapping des informations d'authentification et d'autorisation, et avec les singletons (qui ont révélé leur utilisation après la gratuité).
Je vous apprécie beaucoup, vous et votre travail sur l'OS, mais cette fois, je n'aime pas votre réponse. Elle semble promouvoir le camp du "n'utilisez pas FreeAndNill". Pourtant, comme d'habitude, aucune preuve n'est fournie : trois de vos liens sont tombés ! Le seul qui fonctionne encore pointe vers un article qui est en fait pro FreeAndNil (vrai, pas directement ; il propose d'utiliser FreeAndInvalidate au lieu de FreeAndNil) !
Même si cela ne semble pas très différent de Free, FreeAndNil vous aidera beaucoup dans la recherche de bogues dans votre code.
Comment il sauve vos fesses (accès après gratuit)
Pensez juste à ce qui se passe si vous n'utilisez pas FreeAndNil et que quelque part dans votre code vous accédez à un objet libéré. Si vous avez beaucoup de chance, votre programme se plantera immédiatement lorsqu'il sera exécuté à la maison (oui, ce crash est un 'bon' crash). Si vous êtes un peu malchanceux, il se plantera sur l'ordinateur du client.
Mais ce n'est pas tout. Si vous n'avez pas de chance, le programme ne se plantera pas immédiatement. Ou il ne se plantera jamais, mais il lira ou pire, écrira des morceaux aléatoires de mémoire. C'est là que la vraie douleur commence !
Cela nuira-t-il à votre programme ?
Comme d'autres l'ont déjà souligné, le FreeAndNil lui-même n'est pas mauvais. La fonction FreeAndNil n'est pas cassée/obsolète ou quelque chose comme ça et elle ne corrompra pas votre code. C'est une fonction RTL légitime qui libère un objet et met ensuite son pointeur sur NIL.
Faits et opinions
Certains diront que le fait de s'appuyer sur FreeAndNil peut conduire ou indiquer un défaut de conception, mais ils ne peuvent justifier cela d'aucune manière. C'est juste leur option personnelle. D'un autre côté, un programme qui s'emballe de façon aléatoire et sans débogage (hé hé... je viens de créer un nouveau mot) parce que vous avez refusé d'utiliser FreeAndNil est un fait et non une opinion.
Allez... même sur des variables locales ?
J'utilise FreeAndNil EVEN sur une variable locale. L'utiliser sur une variable locale semble inutile puisque la variable disparaît dès que l'on quitte la procédure.
Pensez que vous pouvez, à une date ultérieure, ajouter du code à la fin de la procédure, APRÈS le point où vous avez libéré l'objet ; du code qui essaiera (accidentellement) d'accéder à l'objet libéré. Si l'objet était NIL, BABUM, instant AV ( exemple ).
Un gros consommateur de ressources ?
Certains programmeurs super-conservateurs diront peut-être que ce simple appel gaspillera de la RAM et des ressources CPU. J'aime aussi livrer des applications petites/rapides/monolithiques !
Mais je ne pense pas que la suppression de l'utilisation de FreeAndNil gaspille plus que quelques octets de RAM et de cycles CPU. Cela ne fera pas une réelle différence dans la vie de tous les jours.
Quand vous pensez qu'une seule ressource graphique comme un glyphe TButton ou l'icône de votre programme peut prendre 50-300KB (et votre programme peut en avoir des dizaines) il est inutile de discuter des ressources 'gaspillées' par FreeAndNil.
Dieux contre programmeurs
Certaines personnes peuvent dire qu'elles n'ont jamais accédé à un objet libre (ce qui signifie qu'elles ne font jamais d'erreurs) et qu'elles n'ont donc pas besoin de FreeAndNil dans ce cas. Eh bien, je ne suis pas un robot. Je fais des erreurs. Je n'ai pas peur de le reconnaître.
Pour
Mason Wheeler a déjà indiqué un article qui montre la méthode de "création paresseuse" qui utilise FreeAndNil et que nous avons tous utilisée un jour : http://tech.turbu-rpg.com/106/delphi-memory-management-made-simple
Cons
Aucune preuve réelle ou solide.
Allen Bauer a un article intitulé Un CASE quand FreeAndNil est votre ennemi suggérant que l'utilisation de FreeAndNil dans un cas très particulier et spécial (le destructeur d'un composant visuel) pourrait être mauvais. Il n'y a pas de code attaché à l'article que l'on puisse compiler, seulement un peu de possible scénarios où FreeAndNil pourrait causer des problèmes.
Allen affirme que FreeAndNil devrait être remplacé par de meilleures méthodes. Par exemple avec FastMM.
Cependant , cette discussion démontre (avec du code compilable) que FastMM échoue alors que FreeAndNil sauve la situation.
Où est la preuve ?
À ce stade, j'adorerais avoir ne serait-ce qu'UN SEUL petit morceau de code concret/compilable pour montrer que FreeAndNil pourrait faire du mal.
D'un autre côté, nous avons beaucoup (voir les liens) d'exemples qui montrent comment FreeAndNil peut nous sauver la mise.
Pour ceux qui sont contre FreeAndNil s'il vous plaît éclairez-nous, en postant un code compilable.
-1. "Toujours" est une erreur. Si vous avez une variable locale dans une méthode, il y a peu ou pas besoin d'utiliser FreeAndNil, parce que la variable n'est accessible que depuis la portée locale. Si vous ne pouvez pas voir que vous n'avez pas besoin de FreeAndNil à cet endroit, vous devez remanier votre code pour réduire le nombre de lignes dans cette méthode. C'est une mauvaise idée de donner une réponse qui dit "toujours" à moins que vous puissiez absolument prouver qu'il n'y a aucune exception à cette règle.
Non, vous ne mettez jamais "accidentellement" une variable locale en variable globale. L'utilisation de variables globales doit être une chose rare et mûrement réfléchie. Utiliser FreeAndNil pour couvrir une possible programmation imprudente est simplement une autre forme de programmation imprudente. Tout appel de fonction a une certaine surcharge (même si elle est négligeable), et même si l'impact peut être faible, il s'agit toujours de cycles CPU inutiles, d'allocation de mémoire, etc. Mon vote est donc toujours valable.
Si tu ne peux pas dire que tu ne jamais mettre accidentellement à niveau un local vers un global et que votre travail à plein temps consiste à écrire du code, vous devriez vous éloigner prudemment du clavier et trouver un nouvel emploi. Je peux honnêtement dire qu'au cours de la dernière décennie, j'ai jamais J'ai accidentellement transformé une variable locale en variable globale et j'ai rencontré un problème dans une méthode qui utilise cette variable locale alors que la solution appropriée serait FreeAndNil. J'ai plusieurs applications de production totalisant bien plus de 5 millions de lignes de code, et un grep de la source trouve deux instances de FreeAndNil dans mon propre code.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
0 votes
@Sertac le fil, comme beaucoup d'autres, a disparu
1 votes
Les nouvelles discussions portent sur la non-technologie ici et ici
0 votes
@mjn : Beaucoup de fils anciens ont disparu après le crash du serveur il y a quelques semaines. Vous avez déjà indiqué les fils les plus récents sur le sujet.
4 votes
Comment se fait-il que je n'aie jamais vu cette question ? :-)
0 votes
Wow. 48 votes et 11 signets. Tant de mythologie autour de cette petite fonction. Peut-être qu'une page d'aide/documentation Delphi appropriée permettra de dissiper une partie du brouillard.