PDA

Ver la Versión Completa : Saber si se ha creado un objeto


Cecilio
13-11-2008, 22:02:00
Hola.

Tengo un TclientDataSet que se crea en tiempo de ejecución, es de ambito global.
Y me gustaría saber si se ha creado previamente.

He probado esto pero no va:

if ds=nil then
ds.create(self)
else
ds.free;

Caro
13-11-2008, 22:06:27
Hola Cecilio, para ello tienes el Assigned.


if Assigned(ClientDataSet) then
showmessage('si')
else
showmessage('no');


Saluditos

Cecilio
13-11-2008, 22:09:06
Caro. Gracias por la prontitud.
:)

roman
14-11-2008, 02:51:22
Assigned es esencialmente lo mismo que la comparación con nil por lo que si falla uno también el otro. Es raro que falle porque siendo una variable global, tendría que inicializarse a nil. Sin embargo, veo que en el código que nos presenta Cecilio usa


ds.create(self)


lo cual es incorrecto para crear el objeto y quizá de ahí venga la falla. Tendría que ser así:


ds := TClientDataSet.create(self)


// Saludos

Neftali
14-11-2008, 09:50:58
También sería bueno, que si en el código lo liberas para volver a crearlo, te asegures de que al liberar coge valor nil.


// para liberar
FreeAndNil(ClientDataSet);

Caro
14-11-2008, 14:59:14
Assigned es esencialmente lo mismo que la comparación con nil por lo que si falla uno también el otro. Es raro que falle porque siendo una variable global, tendría que inicializarse a nil. Sin embargo, veo que en el código que nos presenta Cecilio usa

ds.create(self)
lo cual es incorrecto para crear el objeto y quizá de ahí venga la falla. Tendría que ser así:



Código Delphi [-] (http://www.clubdelphi.com/foros/newreply.php?do=newreply&p=325980#)
ds := TClientDataSet.create(self)


No me había fijado bien como estaba creando su ClientDataSet, ayer respondi rapido y me desconecte despues.

Como te dice el amiguito Roman no debería fallar ya que con el Assigned es como si estuvieras preguntando.


if ClientDataSet<> nil then
//esta creado


La creación de tu ClienDataSet tampoco esta correcta, debes hacerlo como te indica Roman y podrías hacer algo así.


if Assigned(ClientDataSet) then
ClientDataSet.Free; //Si esta creado liberamos

ClientDataSet := TClientDataSet.Create(Self);


Tengo una pregunta yo pense que si no creabas el objeto apuntaba a nil no importando que sea una variable global o una local, acabo de hacer la prueba, si es una variable local no apunta a nil, siendo así con el codigo anterior entra al if libera y al intentar crear te sale un AccessViolation. Estaba haciendo un procedimiento donde mi Objeto es una variable Local y ya me rompiendo mi cabeza porque no estaba funcionando bien, ahora veo que no estaba apuntando a nil, en mi programa en una parte según una condición creaba mi objeto y mas abajo preguntaba si estaba creado entonces que haga algo. ¿Me pueden explicar porque no apunta a nil cuando es variable local, la solución para una variable local sería hacer que ese tu objeto apunte a nil antes de hacer algo?.

Cecilio, libera tu ClientDataSet con FreeAndNil como te dice Neftali.

Saluditos

Lepe
14-11-2008, 15:25:15
No sé el por qué, quizás por eficiencia, tarda menos en ejecutar procedimientos locales porque no tiene que inicializar las variables.

Muchas veces es redundante, delphi la inicializa y nosotros la usamos en un bucle for que también la inicializa.... pues ... ¿pá qué? ;).

Mi comentario es sólo para añadir que pasa con todas las variables locales, incluso un Integer, currency, etc no tendrá el valor cero como puede esperarse.

Saludos

Caro
14-11-2008, 16:01:14
Mi comentario es sólo para añadir que pasa con todas las variables locales, incluso un Integer, currency, etc no tendrá el valor cero como puede esperarse.


Hola Lepe, gracias por responder, se que pasa eso con los demas tipos (integer,......), pero no pense que pasaba lo mismo con un objeto que no ha sido creado aun. Pense, que si defino mi variable, digamos mi ClientDataSet : TClientDAtaSet, con eso tendría una referencia al objeto de tipo TClientDataSet, por lo que pense que esa variable apuntaba a nil, ya que no lo hemos creado todavía.

No hay problema entonces la solución sería cuando tenga un objeto como variable local inicializarlo en nil aunque no lo haya creado, para no tener problemas en el caso que describo en mi anterior mensaje.

Saluditos

donald shimoda
14-11-2008, 16:18:43
Assigned es esencialmente lo mismo que la comparación con nil por lo que si falla uno también el otro. Es raro que falle porque siendo una variable global, tendría que inicializarse a nil.

Con el único fin de enriquecer la charla en tiempo de diseño Assigned triunfa donde pepe <> nil falla.

Documento (http://blogs.codegear.com/abauer/2006/10/31/28836).

Saludos.

donald shimoda
14-11-2008, 16:24:16
ds.create(self)

lo cual es incorrecto para crear el objeto y quizá de ahí venga la falla. Tendría que ser así:

Código Delphi [-] (http://www.clubdelphi.com/foros/#)ds := TClientDataSet.create(self)


// Saludos

Solo con el ánimo de enriquecer la charla (no se puede poner como encabezado? Asi no me meten infracciones los policías del foro :p)

Esto es absolutamente válido :

procedure HacerAlgoyLimpiar;
var
ds : TClientDataSet;
begin
try
ds.create(self);
// hacer algo con DS
finally
FreeAndNil(ds);
end;
end.

Saludos

Lepe
14-11-2008, 17:57:02
OFFTOPIC: Me alegra que te vayas "re-educando", lo digo porque sin el enlace al documento, tendríamos otra divertida polémica.

Te agradezco por mi parte, ese enlace. Primero usaba F <> nil, después de un tiempo me acostumbré a usar Assigned, y desde hace poco dije, ¿qué diferencia hay? pues nada vuelvo a lo de siempre, y como digo, desde hace poco volví a usar F <> nil, ni siquiera lo puse en duda ni busqué en internet.

Desde ahora queda claro que usaré Assigned.

Repito, gracias.

donald shimoda
14-11-2008, 18:00:46
OFFTOPIC: Me alegra que te vayas "re-educando", lo digo porque sin el enlace al documento, tendríamos otra divertida polémica.
;)

Desde ahora queda claro que usaré Assigned.

Repito, gracias.

Gracias por las gracias.:)

roman
14-11-2008, 18:31:24
Assigned es esencialmente lo mismo que la comparación con nil por lo que si falla uno también el otro.


Como dije, Assigned es esencialmente lo mismo que la comparación con nil. Puse la palabra esencial, precisamente porque acababa de ver el artículo que citaron. Aunque si se lee con cuidado, notarán que la diferencia no aplica en el caso de un objeto sino de un apuntador a método.

De todas formas, a mi en lo particular me gusta más usar Assigned en todo caso, por una cuestión de legibilidad, pero eso ya es gusto personal.


Tengo una pregunta yo pense que si no creabas el objeto apuntaba a nil no importando que sea una variable global o una local, acabo de hacer la prueba, si es una variable local no apunta a nil

Así es en efecto Caro, la misma documentación de Delphi señala que las variables locales no se inicializan. La razón la desconozco, aunque pudiera deberse a que esas variables viven en la pila o stack de manera que, en realidad, no hay nada que inicializar sino hasta que se llama la función.


Esto es absolutamente válido :


procedure HacerAlgoyLimpiar;
var
ds : TClientDataSet;
begin
try
ds.create(self);
// hacer algo con DS
finally
FreeAndNil(ds);
end;
end.



No sé a qué te refieres con válido. Quizá te refieras a que compila, lo cual es cierto. Pero para que futuros lectores no se confundan creo que es bueno aclarar que una construcción así es garantía de problemas.

// Saludos

Caro
14-11-2008, 18:37:36
Así es en efecto Caro, la misma documentación de Delphi señala que las variables locales no se inicializan. La razón la desconozco, aunque pudiera deberse a que esas variables viven en la pila o stack de manera que, en realidad, no hay nada que inicializar sino hasta que se llama la función.


Muchísimas gracias por responder Roman ;).

Saluditos

donald shimoda
14-11-2008, 18:38:57
De todas formas, a mi en lo particular me gusta más usar Assigned en todo caso, por una cuestión de legibilidad, pero eso ya es gusto personal.


Opino igual.



No sé a qué te refieres con válido. Quizá te refieras a que compila,


Compila y funciona.Si la idea es usar el objeto de manera temporal cumple con el propósito.


lo cual es cierto. Pero para que futuros lectores no se confundan creo que es bueno aclarar que una construcción así es garantía de problemas.

// Saludos

Podrías explicar porque?

Saludos

roman
14-11-2008, 18:41:04
Podrías explicar porque?


Copiando y pegando tu ejemplo:


---------------------------
Project1
---------------------------
Access violation at address 00403578 in module 'Project1.exe'. Read of address 7A836652.
---------------------------
Aceptar
---------------------------


// Saludos

eduarcol
14-11-2008, 18:53:48
Donald, quizas deberias cambiar esta linea:

ds.create(self);


por esta otra.

ds := TCLientDataset.create(self);

de esta manera estas instanciando la clase dentro de un objeto, en el ejemplo anterior lo asignas como perteneciente a esa clase, pero nunca se inicializa.

roman
14-11-2008, 19:12:52
A manera de explicación a los compañeros que no les quede claro, digamos que un constructor como TObject.Create llama en automático al método de clase NewInstance. Siendo un método de clase, NewInstance (al igual que el constructor) existe independientemente de cualquier instancia de la clase, y es el encargado de asignar memoria para el nuevo objeto y devuelve un apuntador (referencia) a dicho objeto. Ese valor devuelto es el que se asigna a la referencia en una construcción como


ds := TClientDataSet.Create(...);


Si simplemente hacemos


ds.Create(...);


el objeto sí se crea, pero nunca lo asignamos a la variable ds y de ahí todos los riesgo de violación de acceso, pues ds, al no haber sido inicializado, apunta a un lugar "aleatorío" en la memoria (o nil en el caso de variables globales).

En resumen, el objeto existe, pero ds no apunta a él.

// Saludos

eduarcol
14-11-2008, 19:17:01
Bueno Roman, gracias por escribir lo que te explique, no encontraba las palabras, te equivocastes en unas cositas pero no importa :D:p

roman
14-11-2008, 19:21:21
Disculpa Eduardo, trataré de poner más atención la próxima vez :D

// Saludos

donald shimoda
14-11-2008, 19:28:09
Copiando y pegando tu ejemplo:

// Saludos

:( Irrefutable. Me equivoqué feo.

Quería mostrar el caso en que un objeto que se crea y usa de una vez, sin necesidad de acceder mas tarde a este, caso una tarea que se crea para que realice cierto proceso y se destruye sola al terminar en ese caso si es valido algo como:


TMiTarea.create;
No aplica a TClientDataset , ni cerca.

Saludos.

roman
14-11-2008, 19:34:35
¡Ah! Ok. Si todo se hace en el constructor pues es posible que pueda llamarse a Create desde una variable en lugar de referencia de clase misma. Aunque serán casos raros ¿no?

// Saludos

donald shimoda
14-11-2008, 19:38:11
¡Ah! Ok. Si todo se hace en el constructor pues es posible que pueda llamarse a Create desde una variable en lugar de referencia de clase misma. Aunque serán casos raros ¿no?

// Saludos

Si pero se da para una tarea concretamente, que no vaya a ser monitoreada. El create asigna los parámetros de arranque y la dispara con Resume, y se olvida de ella.

las he usado en algunas ocasiones.

Saludos.

maeyanes
14-11-2008, 19:47:46
Hola...

Aquí metiendo mi cuchara :D

En algunas ocasiones se puede hacer para ejecutar hilos (TThread), yo lo he hecho en alguna ocasión, simplemente hago:


TMiHilo.Create(True); // El constructor redefinido Create inicializa la propiedad FreeOnTerminate a True



Saludos...

Delphius
14-11-2008, 19:50:57
Con el único fin de enriquecer la charla en tiempo de diseño Assigned triunfa donde pepe <> nil falla.

Documento (http://blogs.codegear.com/abauer/2006/10/31/28836).

Saludos.

¡Ufff... como tengo oxidado el inglés!:(
Parece que no basta con tener el diccionario de 2 kilos. En algún momento tendré que volver a tomar cursos de inglés.:o

Si no es molestia, ¿Alguien podría resumirlo en castellano?

No me queda muy en claro la relación de dicho documento con lo tratado aquí. Y lo digo precisamente por lo que leo... ¡Habla sobre punteros a métodos! Tal como lo ha señalado roman:

Aunque si se lee con cuidado, notarán que la diferencia no aplica en el caso de un objeto sino de un apuntador a método.



No es por traer problemas, pero es que me gustaría poder entender correctamente la diferencia de emplear Assigned entre objetos y métodos.

Saludos,

roman
14-11-2008, 19:58:48
Je, je, ahora ya estamos con los TThread. Pues sí, es como dices maeyanes. E incluso para otro tipo de situaciones puede definirse un método de clase que se encargue de instanciar un objeto, hacer cositas con él, liberarlo y se acabó.

Por ejemplo, si tenemos una clase TPropiedadesPersona, derivado de TForm para editar las propiedades de un objeto TPersona, puede definirse un método de clase:


type
TPropiedadesPersona = class(TForm)
...
public
property Persona: TPersona .....;
class procedure Editar(P: TPersona);
end;

...

class procedure TPersona.Editar(P: TPersona);
var
Form: TPropiedadesPersona;

begin
Form := TPropiedadesPersona.Create(nil);
Form.Persona := P;

try
Form.ShowModal;
finally
Form.Free;
end;
end;


De esta manera, para editar las propiedades, simplemente haríamos:


TPropiedadesPersona.Editar(P);


sin más nada.

// Saludos

maeyanes
14-11-2008, 20:03:36
Hola

Solo que un método de clase se declara como:


class procedure Editar(P: TPersona);


A menos que esté desactualizado :p



Saludos....

roman
14-11-2008, 20:07:33
Solo que un método de clase se declara como:


Tienes toda la razón. Ya he corregido el ejemplo.

// Saludos

Al González
14-11-2008, 21:33:09
¡Hola a todos!

Mi aportación.

Es un hecho que todas las variables globales están en blanco (en ceros) al inicio del programa.

Las variables locales para tipos de datos que no emplean "finalización automática" no son inicializadas. Esto ahorra instrucciones de código máquina que muchas veces no nos serán necesarias. Se entiende que, en la mayoría de los casos, si declaramos una variable local es porque vamos a asignarle algo antes de utilizarla; aunque es cierto que a veces nos gustaría que se pusieran en blanco desde el inicio de la rutina, logrando que 0, False, Nil, etc. fuera un valor automático predeterminado. Probablemente en el futuro, o en otras plataformas, esta inicialización en ceros de todas las variables locales sea una condición normal, pero de momento, en el Delphi nativo, únicamente ocurre con las cadenas largas (strings), las interfaces, los arreglos dinámicos y los variantes (probado con Delphi 7). Esto es porque la memoria de tales tipos de datos es liberada en automático —o al menos su contador de referencias es disminuido en 1— cuando la función, procedimiento o método termina.

Esta útil practicidad implícita del compilador crea la obligación, para él mismo, de agregar código máquina de "inicialización cruda" al comienzo de la rutina, que consiste simplemente en rellenar de ceros todas las variables locales que sean de esos tipos. Recordemos que una variable local no es más que un grupo de bytes en alguna posición de memoria que pudieron haber sido utilizados antes, por tanto, de entrada, una variable local sin inicializar puede arrojarnos cualquier valor que formen sus bits en ese momento. Si al empezar a ejecutarse una rutina, ésta no hiciera el blanqueo inicial (la inicialización cruda de la que hablo) de una variable local String o arreglo dinámico, por ejemplo, ¿cómo sabría si debe o no ejecutar el código máquina de finalización que el compilador agrega al final, considerando que quizá alguna condición (If) de la rutina determinó que la variable no fuese utilizada? O bien, al asignarle valor por primera vez, ¿cómo sabría el programa que lo que está actualmente en la variable es algo que debe “liberar” antes de realizar la asignación, o sólo se trata de un vestigio del uso que tuvieron esos bytes por parte de otra función?

Es por ello que tales tipos de variables locales, y sólo tales tipos, son puestas en blanco predeterminadamente.

Respecto a los constructores, es bueno aclarar que éstos no son métodos clase, si bien pueden llamarse con la sintaxis de un método clase. Un constructor puede ser llamado tanto con una clase —para crear una instancia e inicializarla— como con una variable o cualquier otra expresión de tipo objeto —para solamente (re)inicializar ese objeto, o hacer lo que está definido en el constructor—. El programa sabe cuándo la llamada ha sido hecha de una manera o de la otra gracias a una bandera interna que se maneja como parámetro implícito (con valor de 1 o 0, respectivamente).

La segunda forma de llamada necesita que la instancia sea real (no basta declarar la variable). Con la primera forma se llama en automático al método virtual NewInstance que señaló Román, para reservar el bloque de memoria donde residirán los campos del objeto, es decir, la instancia. Pero NO es verdad que la segunda forma también cree una instancia. El método NewInstance solamente es ejecutado cuando la llamada al constructor es hecha con una referencia de clase (TClaseObjeto.Create), pero no cuando se realiza con una referencia de objeto (Objeto.Create o Inherited Create). La bandera interna que mencioné antes existe básicamente para saber si NewInstance debe ser llamado o no al ejecutarse un constructor. :)


A value of False in the flag parameter of a constructor call indicates that the constructor was invoked through an instance object or using the inherited keyword. In this case, the constructor behaves like an ordinary method. A value of True in the flag parameter of a constructor call indicates that the constructor was invoked through a class reference. In this case, the constructor creates an instance of the class given by Self, and returns a reference to the newly created object in EAX.


Y en cuanto a utilizar “Assigned (Expresión)” o “Expresión <> Nil”, personalmente prefiero la segunda por parecerme más clara y natural. Aunque tratándose de expresiones método siempre utilizo Assigned, más que nada por impedimentos del compilador para usar la segunda forma.

Espero haber contribuido a algo bueno.

Un abrazo instanciado.

Al González. :)

roman
14-11-2008, 21:47:34
Estupendas aclaraciones Al.

Había olvidado lo del NewInstance en mi afán de entender porqué la llamada a Create desde una variable podría ser válida. Como bien muestras, ni siquiera hay un objeto creado, razón de más para no usar dicha sintaxis para crear un objeto, a no ser que sea, como dices, para reinicializar el objeto.

Es cierto que un constructor no es un método de clase, aunque para los efectos que aquí tratamos, puede entenderse como tal, en el sentido de que existe independientemente de cualquier instancia.

// Saludos

Cecilio
15-11-2008, 13:11:13
Gracias de nuevo a todos.

La verdad es que ya uso assigned(loquesea).
Desde que lo usé no volvió a dar problemas.

Tambien estoy usando lo que aconsejasteis: FreeAndNil para otros casos, no para el que estaba trabajando cuando la consulta en el foro, ya que ese ClientDataset solo se libera al terminar el programa. Cada método comprueba con assigned si está creado y si no lo crea asi:
cds:= TClientDataSet.create(self)

Aunque en el foro puse ds.create(self) realmente nunca lo creo así. Lo puse así por la rapidez ya que lo importante era la comparación a nil del If, ni tampoco lo libero, ya que se libera en onClose de la form.

El programa va de lujo usando el assigned