web-dev-qa-db-fra.com

Comment delete et deleteLater fonctionnent-ils en ce qui concerne les signaux et les slots dans Qt?

Il existe un objet de classe QNetworkRhness. Il y a un slot (dans un autre objet) connecté à son signal fini (). Les signaux sont synchrones (ceux par défaut). Il n'y a qu'un seul fil.

À un moment donné, je veux me débarrasser des deux objets. Plus de signaux ni rien d'eux. Je veux qu'ils disparaissent. Eh bien, je pensais, je vais utiliser

delete obj1; delete obj2;

Mais est-ce que je peux vraiment? Les spécifications pour ~ QObject disent:

La suppression d'un QObject alors que des événements en attente attendent d'être livrés peut provoquer un blocage.

Quels sont les "événements en attente"? Cela pourrait-il signifier que pendant que j'appelle mon delete, il y a déjà des "événements en attente" à livrer et qu'ils peuvent provoquer un plantage et je ne peux pas vraiment vérifier s'il y en a?

Alors disons que j'appelle:

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

Pour être sûr.

Mais suis-je vraiment en sécurité? deleteLater ajoute un événement qui sera géré dans la boucle principale lorsque le contrôle y arrivera. Peut-il y avoir des événements (signaux) en attente pour obj1 ou obj2 déjà là, en attente d'être géré dans la boucle principale avant deleteLater sera géré? Ce serait très malheureux. Je ne veux pas écrire de code vérifiant l'état "quelque peu supprimé" et ignorant le signal entrant dans tous mes emplacements.

72
stach

La suppression de QObjects est généralement sûre (c'est-à-dire dans la pratique normale; il peut y avoir des cas pathologiques que je ne connais pas atm), si vous suivez deux règles de base:

  • Ne supprimez jamais un objet d'un emplacement ou d'une méthode qui est appelé directement ou indirectement par un signal (synchrone, type de connexion "direct") de l'objet à supprimer. Par exemple. si vous avez une classe Operation avec un signal Operation :: done () et un slot Manager :: operationFinished (), vous ne voulez pas supprimer l'objet operation qui a émis le signal dans ce slot. La méthode émettant le signal finish () peut continuer à accéder à "ceci" après l'émission (par exemple accéder à un membre), puis fonctionner sur un pointeur "this" non valide.

  • De même, ne supprimez jamais un objet du code appelé de manière synchrone à partir du gestionnaire d'événements de l'objet. Par exemple. ne supprimez pas un SomeWidget dans son SomeWidget :: fooEvent () ou dans les méthodes/emplacements que vous appelez à partir de là. Le système d'événements continuera de fonctionner sur l'objet déjà supprimé -> Crash.

Les deux peuvent être difficiles à retrouver, car les backtraces semblent généralement étranges (comme un crash lors de l'accès à une variable membre POD), en particulier lorsque vous avez des chaînes de signal/slot compliquées où une suppression peut se produire plusieurs étapes plus bas initiées initialement par un signal ou un événement de l'objet qui est supprimé.

Ces cas sont le cas d'utilisation le plus courant pour deleteLater (). Il s'assure que l'événement en cours peut être terminé avant que le contrôle ne revienne à la boucle d'événements, qui supprime ensuite l'objet. Un autre, je trouve souvent que la meilleure façon est de différer l'action en utilisant une connexion en file d'attente/QMetaObject :: invokeMethod (..., Qt :: QueuedConnection).

66
Frank Osterfeld

Les deux lignes suivantes de vos documents référés indiquent la réponse.

De ~ QObject ,

La suppression d'un QObject pendant que des événements en attente attendent d'être livrés peut provoquer un blocage. Vous ne devez pas supprimer directement le QObject s'il existe dans un thread différent de celui en cours d'exécution. Utilisez plutôt deleteLater (), ce qui provoquera la boucle d'événement pour supprimer l'objet une fois que tous les événements en attente lui ont été remis.

Il nous dit spécifiquement de ne pas supprimer des autres threads. Étant donné que vous avez une seule application filetée, il est sûr de supprimer QObject.

Sinon, si vous devez le supprimer dans un environnement multi-thread, utilisez deleteLater() qui supprimera votre QObject une fois le traitement de tous les événements terminé.

20
liaK

Vous pouvez trouver la réponse à votre question en lisant l'une des règles d'objet Delta qui stipule ceci:

Signal Safe (SS).
Il doit être sûr d'appeler des méthodes sur l'objet, y compris le destructeur, depuis un emplacement appelé par l'un de ses signaux.

Fragment:

À la base, QObject prend en charge la suppression lors de la signalisation. Pour en profiter, il vous suffit de vous assurer que votre objet n'essaie pas d'accéder à l'un de ses propres membres après avoir été supprimé. Cependant, la plupart des objets Qt ne sont pas écrits de cette façon, et il n'est pas nécessaire qu'ils le soient non plus. Pour cette raison, il est recommandé d'appeler toujours deleteLater () si vous avez besoin de supprimer un objet pendant l'un de ses signaux, car il y a de fortes chances que "supprimer" fasse planter l'application.

Malheureusement, il n'est pas toujours clair quand vous devez utiliser ‘delete’ vs deleteLater (). Autrement dit, il n'est pas toujours évident qu'un chemin de code a une source de signal. Souvent, vous pouvez avoir un bloc de code qui utilise "supprimer" sur certains objets qui est sûr aujourd'hui, mais à un moment donné dans le futur, ce même bloc de code finira par être invoqué à partir d'une source de signal et maintenant soudainement votre application plante. La seule solution générale à ce problème consiste à utiliser deleteLater () tout le temps, même si à première vue cela semble inutile.

Généralement, je considère les règles d'objet Delta comme une lecture obligatoire pour chaque développeur Qt. C'est un excellent matériel de lecture.

14
Piotr Dobrogost

Pour autant que je sache, c'est principalement un problème si les objets existent dans différents threads. Ou peut-être pendant que vous traitez réellement les signaux.

Sinon, la suppression d'un QObject déconnectera d'abord tous les signaux et emplacements et supprimera tous les événements en attente. Comme le ferait un appel à déconnecter ().

2
thorsten müller