2010-07-01 14 views
42

¿Cuál es la diferencia básica entre Free y FreeAndNil?¿Qué es preferible: gratis o gratis y nulo?

¿FreeAndNil = Free + Nil?

¿Cuándo debo usar Gratis y cuándo debo usar FreeAndNil?

No estoy recibiendo estos cuando alguien puede ayudarme.

Gracias de antemano.

+1

Una discusión extendida aquí: https://forums.embarcadero.com/thread.jspa?threadID=33112 –

+0

@Sertac el hilo, como muchos otros, desapareció – mjn

+1

Las nuevas discusiones son sobre no tecnología [aquí] (https: //forums.embarcadero.com/thread.jspa?threadID=65321&tstart=15) y [aquí] (https://forums.embarcadero.com/thread.jspa?threadID=63906&tstart=0) – mjn

Respuesta

42

Ver

Y echar un vistazo a la aplicación:

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

Ejemplos

Considere el siguiente código:

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; 

Esto creará un error o un mapa de bits no válido (vacío) en mi escritorio, porque trato de usar un objeto que ha sido liberado Sí, aunque se haya liberado bm, todavía está "asignado", es decir, bm aún apunta a una dirección de memoria, aunque allí no hay nada (utilizable). Para superar esto, uno puede establecer bm := nil, como salvaguarda, Entonces assigned(bm) devolverá falso, como uno quisiera. Más o menos, FreeAndNil(bm) es una abreviatura de bm.Free; bm := nil. La primera instrucción libera toda la memoria (y recursos del SO, tiempo de CPU, etc. utilizados por el objeto) y bm := nil establece el "puntero" bm en nil, de modo que bm ya no apunta al lugar donde solía estar el objeto, pero no más es. De esta forma usted (y rutinas como assigned) no se dejarán engañar al creer que todavía hay un objeto de mapa de bits.

Discusión

Algunos dicen que siempre se debe utilizar en lugar de FreeAndNil(foo)foo.Free. ¿Bueno, por qué no? La instrucción adicional foo := nil probablemente no tome demasiados nanosegundos para ejecutarse, y de hecho assigned(foo) = false es una muy buena propiedad de un objeto liberado. Pero, de nuevo, si sabe lo que está haciendo y sabe que nunca volverá a usar el objeto foo después de liberarlo, entonces podría limitarse a foo.free. Realmente, algunos argumentarían que en muchos casos (pero no en todos), tratar de usar una variable de un objeto liberado es un error en sí mismo. (Por supuesto, hay casos en los que haces esto intencionalmente - que tiene un objeto foo que a veces se asigna y, a veces no lo es.)

+4

"algunos argumentarían que en muchos casos (pero no en todos), intentar usar un objeto que ha sido liberado es un error en sí mismo" - eso siempre es un error, no "a veces". Las variables y los objetos son bestias diferentes: a las variables a veces se les puede asignar "nil", pero eso no es un objeto nil, es solo una variable no asignada. –

+0

@ Cosmin Prund: por supuesto. Voy a cambiar esto. –

+0

@Cosmin - no del todo cierto. Es muy poco probable que las variables que no están asignadas sean NIL a menos que sean cadenas o referencias de interfaz. Por otra parte, los miembros de la clase no asignados serán NIL a menos que usted anule deliberadamente la implementación de NewInstance. – Deltics

17

Básicamente, FreeAndNil establece la referencia a nil y luego libera el objeto. Esto lo marca como no asignado. Entonces, la única razón por la que necesitaría usar FreeAndNil es si su código va a reutilizar la referencia. Si se encuentra en un destructor o en un bloque finally, liberando objetos que nunca volverá a tocar, solo use Free.

Consulte Delphi Memory Management Made Simple para ver un ejemplo de cuándo lo encontré útil. El comentario de Mghie en la parte inferior también vale la pena leerlo.

+4

En realidad, pone la referencia en una var local, nils la referencia y luego libera el objeto usando la var. Local. FreeAndNil debería haber sido nombrado NilAndFree ... :) –

+0

Sí. Destructores incompletos y FreeAndNIL() no son buenos compañeros de cama. – Deltics

+1

@Marjan, recientemente tuve un caso en el que esa nomenclatura probablemente me hubiera ahorrado un tiempo al identificar un error en una implementación singleton demasiado compleja que implicaba acceder a las variables de clase de un destructor que previamente había sido 'FreeAndNil''d ...;) –

8

Voy a responder de una manera diferente.

Tal vez, llenar su referencia de objeto con nil después de liberar su objeto no siempre es una buena idea.

De esta manera, no tiene una distinción entre una referencia que nunca se usó (y por lo tanto es nil), y una referencia que se ha utilizado, pero no se debe usar en el futuro.

Por lo tanto, llenarlo con un número mágico (similar a lo que el administrador de memoria FastMM puede hacer con el contenido de bloques de memoria cuando esos bloques se liberan).

--jeroen

2

Incluso si no parece ser muy diferente de lo gratuito, que le ayudará mucho más tarde mientras que la caza de insectos su código.

Simplemente piense qué sucede si NO usa FreeAndNil y luego en algún lugar de su código accede a un objeto liberado. Si tiene una mala suerte, su programa se bloqueará cuando se ejecute en casa (sí, eso es un accidente 'bueno'). Si tiene un poco de mala suerte, se bloqueará en la computadora del cliente. ¡Entonces comienza el verdadero dolor!

Como ya han señalado otros, el FreeAndNil en sí no está nada mal. La función FreeAndNil no está rota/obsoleta o algo así y no dañará su código. Algunos argumentarán que confiar en FreeAndNil puede conducir o indicar un defecto de diseño.

Uso FreeAndNil EVEN en una variable local. Su uso en una variable local parece SIN PUNTO, ya que la variable desaparece tan pronto como sale del procedimiento. Sin embargo, es una buena práctica que solo le costará unos pocos bytes adicionales. Piense que puede agregar más código al final del procedimiento, DESPUÉS del punto donde liberó el objeto, código que (obviamente accidentalmente) intentará acceder al objeto liberado. Si el objeto era NIL, BABUM, AV instantáneo (example).
Nota: Algunas personas pueden decir que nunca accedieron a ningún objeto gratis (lo que significa que nunca cometerán errores) por lo que no necesitan FreeAndNil en este caso. Bueno, no soy un robot. Yo hago errores.

Algunas personas conservadoras pueden decir que esta simple llamada desperdiciará recursos de RAM y CPU. Nunca diré que la conservación de la memoria/CPU es mala. ¡Me gusta entregar aplicaciones pequeñas/rápidas/monolíticas también!Pero no creo que eliminar usando FreeAndNil desperdicie más de unos pocos bytes y ciclos de CPU (literalmente pocos). No marcará una diferencia real en la vida cotidiana, especialmente cuando crees que un solo recurso gráfico como un glifo TButton o el ícono de tu programa puede tomar de 50 a 300 KB, y tu programa puede tener docenas de estos.

Pros
Mason Wheeler ya apunta a un artículo que muestra el método de 'creación perezosa' que utiliza FreeAndNil que todos utilizamos un día: http://tech.turbu-rpg.com/106/delphi-memory-management-made-simple

Contras
Allen Bauer muestra aquí un ejemplo de cómo usar el FreeAndNil en el caso a DETERMINADO (destructor de un componente visual) y ser realmente malo: http://blogs.embarcadero.com/abauer/2010/02/16/38916
Allen afirma que FreeAndNil debe reemplazarse por mejores métodos. Por ejemplo, con FastMM. Sin embargo, aquí hay un código simple donde FastMM falla mientras FreeAndNil guarda el día: http://codeverge.com/embarcadero.delphi.rtl/freeandnil-to-use-or-not-to-use/1067733

Dado que existen dos argumentos pro y en contra de FreeAndNil, le corresponde a usted decidir si le gusta o no.

+2

-1. "Siempre" está mal. Si tiene una variable local en un método, hay poca o ninguna necesidad de utilizar FreeAndNil, porque solo se puede acceder a la var desde el alcance local. Si no puede ** ver ** que no necesita FreeAndNil allí, necesita refactorizar su código para reducir el número de líneas en ese método. No es una buena idea dar una respuesta que diga "siempre" a menos que pueda demostrar absolutamente que no hay excepciones a esa regla. –

+0

No, nunca "accidentalmente" actualiza un local a uno global. El uso de variables globales debe ser una cosa rara que se considere cuidadosamente. Usar FreeAndNil para cubrir una posible programación descuidada es simplemente otra forma de programación descuidada. Cualquier llamada a la función tiene cierta sobrecarga (incluso si es insignificante), y si bien el impacto puede ser pequeño, sigue siendo innecesarios ciclos de CPU, asignación de memoria, etc. Así que mi voto sigue en pie. –

+0

Si no puede decir que * nunca actualiza accidentalmente un local a un global *, y su trabajo de tiempo completo está escribiendo código, debe retroceder cuidadosamente desde el teclado y buscar un nuevo trabajo. Honestamente puedo decir que en la última década * nunca * accidentalmente actualicé una variable local a una global y tuve un problema en un método que usa esa variable local donde la solución adecuada sería FreeAndNil. Tengo varias aplicaciones de producción que suman más de 5 millones de líneas de código, y un grep de la fuente encuentra dos instancias de FreeAndNil en mi propio código. –