2011-02-03 13 views
61

Hay un objeto de la clase QNetworkReply. Hay una ranura (en algún otro objeto) conectada a su señal terminada(). Las señales son sincrónicas (las predeterminadas). Solo hay un hilo¿Cómo funciona delete and deleteLater en lo que respecta a señales y ranuras en Qt?

En algún momento del tiempo que quieren deshacerse de los dos objetos. No más señales ni nada de ellos. Quiero que se vayan. Bueno, pensé, voy a utilizar

delete obj1; delete obj2; 

Pero ¿Puedo realmente? Las especificaciones para ~ QObject dicen:

Eliminación de un QObject mientras que los eventos pendientes en espera de ser entregado puede causar un accidente.

¿Cuáles son los 'eventos pendientes'? ¿Podría significar que mientras estoy llamando a mi delete, ya hay algunos 'eventos pendientes' para entregar y que pueden causar un bloqueo y realmente no puedo verificar si hay alguno?

Así que digamos que llamo:

obj1->deleteLater(); obj2->deleteLater(); 

para estar seguro.

Pero, ¿estoy realmente a salvo? El deleteLater agrega un evento que se manejará en el bucle principal cuando llegue el control. ¿Puede haber algunos eventos pendientes (señales) para obj1 o obj2 ya está ahí, esperando a ser manejado en el bucle principal antes deleteLater será manejado? Eso sería muy desafortunado. No quiero escribir código para verificar el estado "algo borrado" e ignorar la señal entrante en todas mis ranuras.

+3

Parece 'obj-> disconnect(); obj-> deleteLater(); 'es el camino correcto a seguir: – stach

Respuesta

59

Eliminación QObjects es generalmente seguro (es decir, en la práctica normal, puede haber casos patológicos no tengo conocimiento de la ATM), si se siguen dos reglas básicas:

  • Nunca eliminar un objeto en una ranura o método que es llamado directa o indirectamente por una señal (sincrónica, tipo de conexión "directa") del objeto a eliminar. P. ej. si tiene una operación de clase con una operación de Operación :: finished() y una ranura Manager :: operationFinished(), no desea eliminar el objeto de operación que emitió la señal en esa ranura. El método que emite la señal finished() podría continuar accediendo a "this" después de la emisión (por ejemplo, accediendo a un miembro), y luego operar con un puntero "this" inválido.

  • Del mismo modo, no eliminar un objeto en el código que se llama de forma sincrónica de controlador de eventos del objeto. P.ej. no elimine un SomeWidget en su SomeWidget :: fooEvent() o en los métodos/slots que llama desde allí. El sistema de eventos continuará funcionando en el objeto ya eliminado -> Bloquear.

Ambos pueden ser difíciles de localizar, ya que las trazas generalmente tienen un aspecto extraño (como Crash mientras se accede a una variable miembro POD), especialmente cuando se han complicado las cadenas de señal/ranura en la que una deleción podría ocurrir varios pasos hacia abajo originalmente iniciado por una señal o evento del objeto que se elimina.

Tales casos son el caso de uso más común para deleteLater(). Se asegura de que el evento actual se pueda completar antes de que el control vuelva al bucle de evento, que luego elimina el objeto.Otra, me parece que a menudo es mejor diferir toda la acción utilizando una conexión en cola/QMetaObject :: invokeMethod (..., Qt :: QueuedConnection).

2

Hasta donde yo sé, esto es principalmente un problema si los objetos existen en diferentes hilos. O tal vez mientras estás procesando las señales.

De lo contrario, al eliminar un QObject, primero se desconectarán todas las señales y ranuras y se eliminarán todos los eventos pendientes. Como lo haría una llamada a desconectar().

13

puede encontrar respuesta a su pregunta sobre la lectura de uno de los Delta Object Rules que establece lo siguiente:

de señales de seguridad (SS).
Debe ser seguro para métodos de llamada en el objeto, incluido el destructor, desde dentro de una ranura que se llama por una de sus señales.

Fragmento:

En su esencia, QObject apoya siendo eliminada mientras que la señalización. Con el fin de aprovéchelo solo tiene que asegúrese de que su objeto no intente acceder a ninguno de sus miembros después de siendo eliminado. Sin embargo, la mayoría de los objetos Qt no están escritos de esta manera, y tampoco es obligatorio que sean . Por esta razón, es recomendado que siempre llame al deleteLater() si necesita eliminar un objeto durante una de sus señales, porque las probabilidades son de que 'eliminar' simplemente bloqueará la aplicación.

Desafortunadamente, no siempre es claro cuando debe usar 'eliminar' frente a deleteLater(). Es decir, no es , siempre es obvio que una ruta de código tiene una fuente de señal . A menudo, puede tener un bloque de código que usa 'eliminar' en algunos objetos que es seguro hoy, pero en algún punto en el futuro este mismo bloque de código termina siendo invocado desde una fuente de señal y ahora de repente su aplicación se bloquea. La única solución general a este problema es use deleteLater() todo el tiempo, incluso si a simple vista parece innecesario.

En general considero Reglas de objetos Delta lectura como obligatoria para todos los desarrolladores de Qt. Es un excelente material de lectura.

+1

Si sigue el enlace a DOR, debe seguir los enlaces de esa página para leer más, p. siga el enlace a 'Signal Safe'. La primera página del enlace es difícil de entender sin contexto. (Estoy persiguiendo un bloqueo en la salida usando PyQt en Windows, mi aplicación ni siquiera está eliminando ningún objeto, pero espero que el enlace a DOR ofrezca información.) – bootchk

19

Las siguientes dos líneas de sus documentos referidos dicen la respuesta.

De ~QObject,

Eliminación de un QObject mientras que los eventos pendientes en espera de ser entregado puede causar un accidente. Usted no debe eliminar el QObject directamente si existe en un subproceso diferente al que se está ejecutando actualmente. Utilice deleteLater() en su lugar, lo que hará que el bucle de evento elimine el objeto después de que se le hayan entregado todos los eventos pendientes.

Nos dice específicamente que no eliminemos de otros hilos. Como tiene una sola aplicación con hilos, es seguro eliminar QObject.

De lo contrario, si tiene que eliminarlo en un entorno de subprocesos múltiples, use deleteLater(), que eliminará su QObject una vez que se haya procesado todos los eventos.

+5

¿Qué pasa con mi segundo escenario? ¿Se pueden llamar las ranuras en un objeto después de llamar a deleteLater en él? – stach

Cuestiones relacionadas