Come funziona delete e deleteLater per quanto riguarda i segnali e gli slot in Qt?

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

  •  28-10-2019
  •  | 
  •  

Domanda

Esiste un oggetto della classe QNetworkReply. C'è uno slot (in qualche altro oggetto) collegato al suo segnale finito (). I segnali sono sincroni (quelli di default). C'è solo un thread.

Ad un certo punto voglio sbarazzarmi di entrambi gli oggetti. Niente più segnali o altro da loro. Voglio che se ne vadano. Bene, ho pensato, userò

delete obj1; delete obj2;

Ma posso davvero? Le specifiche per ~ QObject dicono:

L'eliminazione di un QObject mentre gli eventi in sospeso sono in attesa di essere consegnati può causare un arresto anomalo.

Cosa sono gli "eventi in sospeso"? Ciò potrebbe significare che mentre sto chiamando il mio delete, ci sono già alcuni "eventi in sospeso" da consegnare e che potrebbero causare un arresto anomalo e non posso davvero verificare se ce ne sono?

Quindi diciamo che chiamo:

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

Per essere al sicuro.

Ma sono davvero al sicuro? Il deleteLater aggiunge un evento che verrà gestito nel ciclo principale quando il controllo arriva. Possono esserci già alcuni eventi in sospeso (segnali) per obj1 o obj2, in attesa di essere gestiti nel loop principale prima di deleteLater? Sarebbe molto sfortunato. Non voglio scrivere codice per verificare lo stato "in qualche modo cancellato" e ignorare il segnale in arrivo in tutti i miei slot.

È stato utile?

Soluzione

L'eliminazione di QObjects è generalmente sicura (ovvero nella pratica normale; potrebbero esserci casi patologici di cui non sono a conoscenza atm), se segui due regole di base:

  • Non eliminare mai un oggetto in uno slot o metodo chiamato direttamente o indirettamente da un segnale (sincrono, tipo di connessione "diretto") dall'oggetto da eliminare. Per esempio. se si dispone di una classe Operation con un segnale Operation :: finish () e uno slot Manager :: operationFinished (), non si desidera eliminare l'oggetto operazione che ha emesso il segnale in quello slot. Il metodo che emette il segnale finish () potrebbe continuare ad accedere a "this" dopo l'emit (ad es. Accedere a un membro), e quindi operare su un puntatore "this" non valido.

  • Allo stesso modo, non eliminare mai un oggetto nel codice che viene chiamato in modo sincrono dal gestore di eventi dell'oggetto. Per esempio. non eliminare un SomeWidget nel suo SomeWidget :: fooEvent () o nei metodi / slot che chiami da lì. Il sistema degli eventi continuerà a funzionare sull'oggetto già eliminato -> Crash.

Entrambi possono essere difficili da rintracciare, poiché i backtrace di solito sembrano strani (come il crash durante l'accesso a una variabile membro POD), specialmente quando si hanno catene di segnali / slot complicate in cui una cancellazione potrebbe avvenire diversi passaggi in basso originariamente iniziati da un segnale o evento dall'oggetto che viene eliminato.

Questi casi sono il caso d'uso più comune per deleteLater (). Si assicura che l'evento corrente possa essere completato prima che il controllo ritorni al ciclo di eventi, che quindi elimina l'oggetto. Un altro, che trovo spesso un modo migliore, è rimandare l'intera azione utilizzando una connessione in coda / QMetaObject :: invokeMethod (..., Qt :: QueuedConnection).

Altri suggerimenti

Le due righe successive dei documenti segnalati indicano la risposta.

Da ~ QObject ,

L'eliminazione di un QObject mentre gli eventi in sospeso sono in attesa di essere consegnati può causare un arresto anomalo. Non devi eliminare direttamente QObject se esiste in un thread diverso da quello attualmente in esecuzione. Utilizza invece deleteLater (), che farà sì che il ciclo di eventi elimini l'oggetto dopo che tutti gli eventi in sospeso sono stati consegnatiad esso.

Ci dice specificamente di non eliminare da altri thread.Poiché hai una singola applicazione a thread, è sicuro eliminare QObject.

Altrimenti, se devi eliminarlo in un ambiente multi-thread, usa deleteLater() che cancellerà il tuo QObject una volta completata l'elaborazione di tutti gli eventi.

Puoi trovare la risposta alla tua domanda leggendo una delle Regole degli oggetti delta che afferma questo:

Signal Safe (SS).
Deve essere sicuro metodi di chiamata sull'oggetto, inclusi il distruttore, dall'interno di uno slot essere chiamato da uno dei suoi segnali.

Frammento:

Fondamentalmente, QObject supporta l'essere cancellato durante la segnalazione. In modo da approfittane devi solo farlo assicurati che il tuo oggetto non ci provi accedere a uno dei suoi membri dopo essere cancellato. Tuttavia, la maggior parte di Qt gli oggetti non vengono scritti in questo modo e non è necessario che lo siano o. Per questo motivo lo è ti consiglio di chiamare sempre deleteLater () se è necessario eliminare un file oggetto durante uno dei suoi segnali, perché le probabilità sono che "cancella" lo farà basta mandare in crash l'applicazione.

Sfortunatamente, non è sempre chiaro quando dovresti usare "cancella" e deleteLater (). Cioè, non lo è sempre ovvio che un percorso di codice ha un file sorgente del segnale. Spesso potresti avere un file blocco di codice che utilizza "elimina" su alcuni oggetti che oggi sono al sicuro, ma a un certo punto in futuro questo stesso blocco di codice finisce per essere invocato da una sorgente di segnale e ora all'improvviso la tua applicazione si blocca. Il solo La soluzione generale a questo problema è usa deleteLater () tutto il tempo, anche se a prima vista sembra inutile.

In genere considero le Regole degli oggetti delta una lettura obbligatoria per ogni sviluppatore Qt. È un eccellente materiale di lettura.

Per quanto ne so, questo è principalmente un problema se gli oggetti esistono in thread diversi.O forse mentre stai effettivamente elaborando i segnali.

In caso contrario, l'eliminazione di un QObject disconnetterà prima tutti i segnali e gli slot e rimuoverà tutti gli eventi in sospeso.Come farebbe una chiamata a disconnect ().

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top