2010-05-27 8 views
7

Tengo una pregunta general sobre las mejores prácticas en OO Delphi. Actualmente, pongo bloques try-finally en cualquier lugar donde creo un objeto para liberar ese objeto después de su uso (para evitar fugas de memoria). Ej .:¿Debería poner un bloque try-finally después de cada Object.Create?

aObject := TObject.Create; 
try 
    aOBject.AProcedure(); 
    ... 
finally 
    aObject.Free; 
end; 

en lugar de:

aObject := TObject.Create; 
aObject.AProcedure(); 
.. 
aObject.Free; 

¿Cree que es una buena práctica, o demasiado trabajo? ¿Y qué hay del rendimiento?

Respuesta

15

En mi opinión, no es más que una de las razones de una construcción de objetos no debe ser seguido por (or "in" as Mason pointed out) un bloque try/finally.

  1. Si la vida de un objeto está siendo administrada por otro objeto.

Esta gestión puede tomar tres formas:

  1. referencia de un objeto tiene un alcance más allá del bloque local y se libera otra parte - como un miembro campo liberado en el destructor.
  2. Objeto agregado inmediatamente a una lista que se encarga de liberar el objeto más tarde.
  3. Un objeto con un administrador de por vida asociado, por ejemplo, cómo pasar el propietario a un control de VCL en la construcción.

Con # 1, cuando la referencia tiene un alcance más amplio, la referencia debe regularse a cero inmediatamente si no se construye de inmediato. De esa manera, cuando se verifique para referencia, sabrá que tiene una lectura precisa. Esto es más común con los objetos miembros que se construyen como parte de una clase más grande, y luego se limpian cuando se destruye el objeto principal.

Con # 2, cuando se añade un objeto a una lista, que desea utilizar un bloque try-except (una de las pocas veces que uso uno) por si acaso se produce una excepción después de que el objeto se construye y se antes de que se agregue a la lista de administración. Idealmente, la primera línea después de la construcción es agregarla a la lista, o la lista es en realidad una clase de fábrica que le da un objeto ya agregado a la lista.

Con # 3, cuando un objeto tiene otro gestor de toda la vida, que realmente debe asegurarse de que la maneje que el manager es lo que hay que hacer. Si está construyendo un control de VCL, puede tener la tentación de tener el formulario (o cualquier otro control) de su propiedad, pero eso realmente implica una sobrecarga adicional en la construcción y la destrucción. Si es posible, explícitamente debes liberarlo, esto es especialmente cierto si pones el control una vez, entonces sabes que lo liberarás en el destructor de tu formulario o cuando se cierre. La única vez que no puede hacer esto es si la creación de control es más dinámica.

Así que sí, es una buena práctica usar muchos bloques try/finally. Solo debe tener unos pocos bloques try/except, y la mayoría de ellos deberían atrapar tipos de excepción muy específicos y/o volver a generar la excepción. Si tiene más bloques try/except que try/finally, entonces lo está haciendo incorrectamente.

+2

Muy buena respuesta. Y usted incluyó algunas cosas interesantes y destacadas que el OP no pensó preguntar. Como el problema de tener DEMASIADOS bloques gruesos "try..catch" que efectivamente matan su capacidad para administrar fallas de código, y salir limpiamente de las llamadas call-stack fallidas. –

17

Definitivamente es una buena práctica usar try-finally.

En caso de que se produzca una excepción, se liberará ese objeto .

En cuanto a rendimiento: medida antes de optimizar.

+3

+1 para la tercera frase. –

13

Como dijo Frank, "en cuanto al rendimiento: mida antes de optimizar". Repitiéndolo para enfatizar.

Además, si está creando un grupo de objetos en un método, no necesita usar un bloque try..finally para cada uno de ellos. Eso puede conducir a un lío de sangría feo. create, try, create, try, create, try, do something, finally, free, finally, free, finally, free. ¡Uf! En su lugar, puede establecer las referencias de objeto a nil en la parte superior del método, luego créelos todos, haga uno try block, y libérelos todos en la sección finally.

Eso ahorrará algo de sobrecarga y rendimiento (aunque probablemente nunca notará la diferencia) pero, lo que es más importante, hará que su código sea más limpio y fácil de leer, manteniendo el mismo nivel de seguridad.

+1

+1 para repetición :) Además, si está creando tantos objetos que tiene un problema de indentación, tiene un problema mayor: su método está haciendo demasiado. –

+0

+1 Anidado try/finally para cada object.create is fugly. 1 por bloque suele ser suficiente. –

+4

Si uno de los destructores arroja una excepción, los siguientes no se liberarán. Tidy ... tal vez, pero no creo que sea una buena práctica. –

5

Para responder a la segunda parte de su pregunta:
try finally apenas tiene sobrecarga.

De hecho, hay muchos métodos que tienen un intento implícito ... finalmente bloquear. Por ejemplo, simplemente tener una función usando una var local de cualquier tipo de interfaz y asignarle un valor.

--jeroen

+1

... o una cadena, para el caso. –

+0

Quise dar a entender el bajo costo de una prueba/finalmente, pero obviamente no tuvo éxito :) Es como preocuparse por el rendimiento de una llamada a procedimiento. –

+0

@Frank: no se preocupe, he votado a favor su respuesta. @Mason: de hecho. –

2

Si va a crear el objeto en el constructor de una clase y el objeto será propiedad de la clase envolvente, tendrá que liberar en destructor de la clase propietaria.

Tiendo a utilizar FreeAndNil() en lugar de llamar a Free.

EDITAR: Pero como otros han dicho, definitivamente siempre quieres liberar lo que creas.

5

El año pasado en Delphi Developer days Vi un poco del alijo privado de código de Marco Cantu y él llama a try finalmente cada vez que crea algo.

Alguien le preguntó al respecto, y él dijo que intenta hacerlo todo el tiempo.

Pero es especialmente una buena idea para el código de subprocesos múltiples cuando se trata de ingresar y salir de secciones críticas, aunque eso no es sobre el tema, es algo bueno de recordar.

Obviamente, a veces es un poco molesto y si no está en la cultura de su entorno de trabajo estar al tanto de su robsutness, puede hacer que se vea como un regalo-dos-zapatos. Pero creo que es una buena idea. Es algo así como el intento de Delphi en la recolección manual forzada de basura.

+1

"pero es especialmente una buena idea para el código de subprocesos múltiples". - No tiene absolutamente nada que ver con el código de subprocesos múltiples. Es * esencial * todo el tiempo, no importa si es una buena idea o mejor en una circunstancia u otra. Si le resulta molesto, entonces debería considerar algo así como mi enfoque de AutoFree(): http://www.deltics.co.nz/blog/?p=391 – Deltics

+0

Estoy totalmente en desacuerdo, aunque sí usted no crea sus hilos y libéralos con bloques try/finally, pero quieres entrar en tus secciones críticas y salir de ellas con try/finallys. Voy a modificar mi respuesta. –

-4

Incluso si es muy recomendado para hacer eso, no siempre lo haré.

Mis reglas de usar o no usar el try/finally:

  • La posibilidad de que el objeto a estrellarse y arder.
  • Número de veces que se crea el objeto. Si sé que su objeto se crea raramente (no más de 5-6 veces en el tiempo de vida de la aplicación) puedo vivir con una pérdida de memoria de 20 KB, en caso de que 'muera' sin ser liberado.
  • Cantidad de memoria filtrada en caso de que el objeto falle.
  • Complejidad del código. Try/except hace que el código se vea realmente feo. Si hay un procedimiento de 5 líneas, siempre uso try/except.
  • Extensión de archivo de aplicación. Si su aplicación necesita ejecutarse durante días, definitivamente quiero liberar CUALQUIER pieza de memoria, de lo contrario la fuga se acumulará.

El único lugar donde es difícil tomar una decisión es cuando necesita rendimiento mientras crea el objeto miles de veces (en un ciclo, por ejemplo). En este caso, no uso try/excepto si el objeto está realizando tareas simples y hay una pequeña posibilidad de verlo estrellarse.

+0

Totalmente en desacuerdo. Es un antipatrón y solo debe usarse en casos excepcionales. –

+0

Espero que no esté diseñando sistemas operativos. En mi computadora, Windows 7 a menudo se ejecuta durante meses. –

+0

-1 Así que realmente piensas en todas estas preguntas (y probablemente sacas algunas conclusiones equivocadas en el camino) cada vez que creas un objeto en lugar de simplemente hacer un hábito para usar 'try..finally' .. No lo hago obténgalo ... btw: el tema es 'try..finally' not' try..except'. – jpfollenius

1

Sí, siempre es una buena idea (esencial) si el código que crea el objeto es responsable de liberarlo. Si no, entonces try/finally no es apropiado, pero tampoco lo es. Free en cualquier caso.

Sin embargo, puede ser engorroso tener este código repetitivo acribillar su "lógica comercial", y es posible que desee considerar un enfoque que tenga la misma garantía de liberar sus objetos pero que sea mucho más limpio (y tenga otros beneficios) , such as my own AutoFree() implementation.

Con AutoFree() su código a continuación, se puede escribir:

aObject := TObject.Create; 
AutoFree(@aObject); 

aObject.AProcedure(); 

o, como alternativa, ya que la aplicación utiliza una referencia a una referencia (para permitir la auto-NIL'ing, así como Free'ing), incluso puede prerregistrar sus referencias que desea que se desactiven automáticamente para mantener esas declaraciones de limpieza fuera de la lógica de su negocio y mantener la "carne" real de su código lo más limpia posible (esto es especialmente beneficioso cuando se necesitan objetos potencialmente múltiples) para ser Free'd):

AutoFree(@aObject1); 
AutoFree(@aObject2); 

aObject1 := TObject.Create; 
aObject1.AProcedure(); 

// Later on aObject2 is (or may be) also created 
: 

No se muestra en mi publicación original, es una adición posterior al mecanismo para admitir el registro de múltiples referencias en una sola llamada de AutoFree(), pero estoy seguro de que puede averiguar los cambios necesarios para sustentar esto por su cuenta, si desea ser capaz de hacer esto:

AutoFree([@aObject1, @aObject2]); 

aObject1 := TObject.Create; 
aObject1.AProcedure(); 

// Later on aObject2 is (or may be) also created 
: 
Cuestiones relacionadas