¿Cómo funciona delete y deleteLater con respecto a las señales y ranuras en Qt?

StackOverflow https://stackoverflow.com/questions/4888189

  •  28-10-2019
  •  | 
  •  

Pregunta

Hay un objeto de clase QNetworkReply. Hay una ranura (en algún otro objeto) conectada a su señal de terminado (). Las señales son síncronas (las predeterminadas). Solo hay un hilo.

En algún momento quiero deshacerme de ambos objetos. No más señales ni nada de ellos. Quiero que se vayan. Bueno, pensé, usaré

delete obj1; delete obj2;

¿Pero puedo realmente? Las especificaciones de ~ QObject dicen:

Eliminar un QObject mientras hay eventos pendientes a la espera de ser entregados puede causar un bloqueo.

¿Qué son los 'eventos pendientes'? ¿Podría eso significar que mientras llamo a mi delete, ya hay algunos 'eventos pendientes' por entregar y que pueden causar un bloqueo y realmente no puedo verificar si hay alguno?

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 el control llegue allí. ¿Puede haber algunos eventos pendientes (señales) para obj1 o obj2 ya allí, esperando ser manejados en el bucle principal antes de que se maneje deleteLater? Eso sería muy lamentable. No quiero escribir código comprobando el estado 'algo eliminado' e ignorando la señal entrante en todas mis ranuras.

¿Fue útil?

Solución

Eliminar QObjects suele ser seguro (es decir, en la práctica normal; puede haber casos patológicos de los que no tengo conocimiento), si sigue dos reglas básicas:

  • Nunca elimine un objeto en una ranura o método que sea llamado directa o indirectamente por una señal (síncrona, tipo de conexión "directa") del objeto a eliminar. P.ej. si tiene una clase Operation con una señal Operation :: finalizado () y un slot Manager :: operationFinished (), no desea eliminar el objeto de operación que emitió la señal en ese slot. El método que emite la señal finalizada () puede continuar accediendo a "esto" después de la emisión (por ejemplo, accediendo a un miembro) y luego operar en un puntero "esto" no válido.

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

Ambos pueden ser difíciles de rastrear, ya que los backtraces generalmente se ven extraños (como un bloqueo al acceder a una variable miembro de POD), especialmente cuando tiene cadenas de señales / ranuras complicadas donde una eliminación puede ocurrir varios pasos hacia abajo originalmente iniciados por una señal o evento del objeto que se elimina.

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

Otros consejos

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

De ~ QObject ,

Eliminar un QObject mientras los eventos pendientes están esperando ser entregados puede causar un bloqueo. No debes eliminar el QObject directamente si existe en un hilo diferente al que se está ejecutando actualmente. Usa deleteLater () en su lugar, lo que hará que el bucle de eventos elimine el objeto después de que se hayan entregado todos los eventos pendientesa él.

Específicamente nos dice que no eliminemos de otros hilos.Dado que tiene una aplicación de un solo subproceso, es seguro eliminar QObject.

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

Puede encontrar la respuesta a su pregunta leyendo acerca de una de las Reglas de objetos delta que dice esto:

Signal Safe (SS).
Debe ser seguro métodos de llamada en el objeto, incluyendo el destructor, desde dentro de una ranura siendo llamado por una de sus señales.

Fragmento:

En esencia, QObject admite ser eliminado durante la señalización. A fin de que aprovéchalo solo tienes que asegúrese de que su objeto no intente acceder a cualquiera de sus propios miembros después siendo eliminado. Sin embargo, la mayora de Qt los objetos no se escriben de esta manera, y no hay ningún requisito para que sean ya sea. Por esta razón, es recomendó que siempre llames deleteLater () si necesita eliminar un objeto durante una de sus señales, porque lo más probable es que "eliminar" simplemente bloquea la aplicación.

Desafortunadamente, no siempre está claro cuándo debe usar "eliminar" vs deleteLater (). Es decir, no es siempre es obvio que una ruta de código tiene una fuente de señal. A menudo, es posible que tenga un bloque de código que usa "eliminar" en algunos objetos que están a salvo hoy, pero en algún momento en el futuro este mismo el bloque de código termina siendo invocado de una fuente de señal y ahora de repente tu aplicación falla. Lo único La solución general a este problema es use deleteLater () todo el tiempo, incluso si de un vistazo parece innecesario.

En general, considero que las Reglas de objetos Delta son de lectura obligatoria para todos los desarrolladores de Qt. Es un excelente material de lectura.

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

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top