2010-07-01 5 views
42

Quelle est la différence fondamentale entre Free et FreeAndNil?Qu'est-ce qui est préférable: Free ou FreeAndNil?

Est-ce que FreeAndNil = Free + Nil?

Quand devrais-je utiliser Free et quand devrais-je utiliser FreeAndNil?

Je ne reçois pas ces derniers lorsque quelqu'un peut m'aider.

Merci d'avance.

+1

Une discussion étendue ici: https://forums.embarcadero.com/thread.jspa?threadID=33112 –

+0

@Sertac le fil, comme beaucoup d'autres, a disparu – mjn

+1

Nouvelles discussions sont sur la non-technologie [ici] (https: //forums.embarcadero.com/thread.jspa?threadID=65321&tstart=15) et [ici] (https://forums.embarcadero.com/thread.jspa?threadID=63906&tstart=0) – mjn

Répondre

42

Voir

Et un regard sur la mise en œuvre:

procedure FreeAndNil(var Obj); 
var 
    Temp: TObject; 
begin 
    Temp := TObject(Obj); 
    Pointer(Obj) := nil; 
    Temp.Free; 
end; 

Exemples

Considérez 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 va créer une erreur ou un bitmap invalide (vide) sur mon bureau, parce que j'essaie d'utiliser un objet qui a été libéré. Oui, même si bm a été libéré, il est toujours "affecté", c'est-à-dire bm pointe toujours vers une adresse de mémoire, même s'il n'y a rien (utilisable) là. Pour surmonter cela, on peut définir bm := nil, comme une sauvegarde, alors assigned(bm) retournera faux, comme on le voudrait. Plus ou moins, FreeAndNil(bm) est un raccourci pour bm.Free; bm := nil. La première instruction libère toute la mémoire (et les ressources du système d'exploitation, le temps processeur utilisé par l'objet), et bm := nil définit le "pointeur" bm à nil, de sorte que bm ne pointe plus à l'endroit où l'objet était, mais pas c'est plus long. De cette façon vous (et les routines comme assigned) ne serez pas dupé à croire qu'il existe toujours un objet bitmap.

Discussion

Certains disent que vous devriez toujours utiliser FreeAndNil(foo) plutôt que foo.Free. Eh bien pourquoi pas? L'instruction supplémentaire foo := nil ne prendra probablement pas trop de nanosecondes à 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 sachez que vous n'allez plus jamais utiliser l'objet foo après l'avoir libéré, alors vous pouvez vous en tenir à foo.free. Vraiment, certains diront 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 faites cela intentionnellement - vous avez un objet foo qui est attribué et est parfois parfois.)

+4

" 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 des bêtes différentes: les variables peuvent parfois être assignées "nil", mais ce n'est pas un objet nul, c'est juste une variable non assignée. –

+0

@Cosmin Prund: Bien sûr. Je vais changer ça. –

+0

@Cosmin - pas tout à fait vrai. Les variables qui ne sont pas affectées sont hautement improbables d'être NIL sauf s'il s'agit de chaînes ou de références d'interface. D'autre part, les membres de classe non affectés seront certainement NIL à moins que vous ne remplaciez délibérément l'implémentation de NewInstance. – Deltics

17

Fondamentalement, FreeAndNil définit la référence à zéro puis libère l'objet. Cela le marque comme non affecté. Donc, la seule raison pour laquelle vous auriez besoin d'utiliser FreeAndNil est si votre code va réutiliser la référence. Si vous êtes dans un destructeur ou un finalement bloquer, libérant des objets que vous n'allez plus jamais toucher, utilisez Free.

Voir Delphi Memory Management Made Simple pour un exemple de quand je l'ai trouvé utile. Le commentaire de Mghie en bas vaut aussi la peine d'être lu.

+4

En fait, il place la référence dans une variable locale, nils la référence puis libère l'objet en utilisant la variable locale. FreeAndNil aurait dû s'appeler NilAndFree ... :) –

+0

Yep. Les destructeurs incomplets et FreeAndNIL() ne sont pas de bons compagnons. – Deltics

+1

@ Marjan, J'ai récemment eu un cas où ce nommage m'aurait probablement sauvé un certain temps en identifiant un bogue dans une implémentation de singleton trop complexe qui impliquait l'accès à des variables de classe à partir d'un destructeur précédemment 'FreeAndNil''d ...;) –

8

Je vais répondre d'une manière différente.

Peut-être que remplir 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 est donc nil) et une référence qui a été utilisée, mais qui ne devrait pas être utilisée dans le futur. Donc, en le remplissant avec un nombre magique (similaire à ce que le gestionnaire de mémoire FastMM peut faire avec le contenu des blocs de mémoire lorsque ces blocs sont libérés).

--jeroen

2

Même s'il ne semble pas être très différent de celui gratuit, il vous aidera beaucoup plus tard pendant la chasse aux bugs votre code. Imaginez ce qui se passe si vous n'utilisez pas FreeAndNil et que vous accédez à un objet libéré quelque part dans votre code. Si vous êtes terriblement chanceux, votre programme va planter quand il fonctionne à la maison (oui c'est crash est un «bon» accident). Si vous êtes un peu malchanceux, il va planter dans l'ordinateur du client. Alors la vraie douleur commence!

Comme d'autres déjà pointé, 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 ne corrompra pas votre code. Certains diront que s'appuyer sur FreeAndNil peut conduire ou indiquer un défaut de conception. J'utilise FreeAndNil EVEN sur une variable locale. L'utiliser sur une variable locale semble POINTLESS puisque la variable disparaît dès que vous quittez la procédure. Cependant, les bonnes pratiques ne vous coûteront que quelques octets supplémentaires. Pensez que vous pourrez ultérieurement ajouter du code à la fin de la procédure, APRÈS le moment où vous avez libéré l'objet, code qui tentera (évidemment accidentellement) d'accéder à l'objet libéré. Si l'objet était NIL, BABUM, AV instantané (example).
Note: Certaines personnes peuvent dire qu'elles n'ont jamais accédé à un objet libre (ce qui signifie qu'elles ne font jamais d'erreur), donc elles n'ont pas besoin de FreeAndNil dans ce cas. Eh bien, je ne suis pas un robot. Je fais des erreurs. Certaines personnes conservatrices peuvent dire que cet appel simple gaspillera des ressources RAM et CPU. Je ne dirai jamais que conserver la mémoire/CPU est mauvais. J'aime délivrer des applications petites/rapides/monolithiques aussi!Mais je ne pense pas que la suppression en utilisant FreeAndNil va gâcher plus de quelques octets et cycles de CPU (littéralement peu). Cela ne fera aucune différence dans la vie de tous les jours, surtout si vous pensez qu'une seule ressource graphique comme un glyphe TButton ou l'icône de votre programme peut prendre entre 50 et 300 Ko, et votre programme peut en avoir des douzaines.

Pros
Mason Wheeler points qui ont déjà un article qui montre la méthode « création paresseuse » qui utilise FreeAndNil que nous avons tous utilisé l'un jour: http://tech.turbu-rpg.com/106/delphi-memory-management-made-simple

Contre
Allen Bauer montre ici un exemple sur la façon dont l'utilisation de la FreeAndNil dans un cas CERTAIN (destructeur d'un composant visuel) et être en effet mauvais: http://blogs.embarcadero.com/abauer/2010/02/16/38916
Allen déclare que FreeAndNil devrait être remplacé par de meilleures méthodes. Par exemple avec FastMM. Cependant, voici un simple morceau de code où FastMM échoue alors que FreeAndNil sauve le jour:

Puisqu'il y a deux arguments pour et contre FreeAndNil, c'est à vous de décider si vous le souhaitez ou non.

+2

-1. "Toujours" est faux. Si vous avez une variable locale dans une méthode, il y a peu ou pas besoin d'utiliser FreeAndNil, car la var n'est accessible qu'à partir de la portée locale. Si vous ne pouvez pas ** voir ** que vous n'avez pas besoin de FreeAndNil, vous devez refactoriser votre code pour réduire le nombre de lignes dans cette méthode. C'est une mauvaise idée de toujours donner une réponse qui dit «toujours» à moins que vous ne puissiez absolument prouver qu'il n'y a aucune exception à cette règle. –

+0

Non, vous ne faites jamais de mise à niveau "accidentelle" d'un local vers un global. L'utilisation de variables globales devrait être une chose rare qui est soigneusement considérée. Utiliser FreeAndNil pour couvrir une éventuelle 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 bien que l'impact puisse être faible, il reste des cycles CPU inutiles, l'allocation de mémoire, etc. Donc, mon vote est toujours valide. –

+0

Si vous ne pouvez pas dire que vous n'avez jamais mis à niveau un local vers un global et que votre travail à plein temps est en train d'écrire du code, vous devez revenir du clavier et trouver un nouveau travail. Je peux honnêtement dire qu'au cours de la dernière décennie, je n'ai * jamais * mis à jour accidentellement une variable locale et que j'ai eu un problème avec une méthode qui utilise cette variable locale où la bonne solution 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. –

Questions connexes