In che modo delete e deleteLater funzionano in relazione a segnali e slot in Qt?

Esiste un object di class QNetworkReply. C’è uno slot (in qualche altro object) collegato al suo segnale finito (). I segnali sono sincroni (quelli di default). C’è solo una discussione.

In qualche momento voglio liberarmi di entrambi gli oggetti. Niente più segnali o niente da loro. Li voglio spariti. Beh, ho pensato, userò

delete obj1; delete obj2; 

Ma posso davvero? Le specifiche per ~ QObject dicono:

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

Quali sono gli “eventi in sospeso”? Potrebbe significare che mentre sto chiamando la mia delete , ci sono già alcuni “eventi in sospeso” da consegnare e che potrebbero causare un crash e non posso davvero controllare se ce ne sono?

Quindi diciamo che chiamo:

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

Per essere al sicuro.

Ma sono davvero al sicuro? deleteLater aggiunge un evento che verrà gestito nel ciclo principale quando il controllo arriva. Possono esserci già degli eventi in sospeso (segnali) per obj1 o obj2 già lì, in attesa di essere gestiti nel ciclo principale prima che venga cancellato deleteLater? Sarebbe molto sfortunato. Non voglio scrivere il controllo del codice per lo stato ‘un po’ cancellato ‘e ignorare il segnale in arrivo in tutti i miei slot.

Cancellare QObjects è di solito sicuro (cioè nella pratica normale, potrebbero esserci casi patologici di cui non sono a conoscenza), se si seguono due regole di base:

  • Non eliminare mai un object in uno slot o metodo chiamato direttamente o indirettamente da un segnale (sincrono, tipo di connessione “diretto”) dall’object da eliminare. Ad esempio, se si dispone di un’operazione di class con un segnale Operation :: finished () e uno slot Manager :: operationFinished (), non si desidera eliminare l’object operazione che ha emesso il segnale in tale slot. Il metodo che emette il segnale finished () potrebbe continuare ad accedere a “this” dopo l’emit (ad es. Accedere ad un membro), e quindi operare su un “this” pointer non valido.

  • Allo stesso modo, non eliminare mai un object nel codice che viene chiamato in modo sincrono dal gestore di eventi dell’object. Ad 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’object già eliminato -> Crash.

Entrambe possono essere difficili da rintracciare, poiché le backtrace di solito appaiono strane (come crash durante l’accesso a una variabile membro POD), specialmente quando si hanno catene segnale / slot complicate in cui potrebbe verificarsi una cancellazione di alcuni passi verso il basso originariamente avviati da un segnale o evento da l’object che è stato cancellato.

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 degli eventi, che quindi cancella l’object. Un altro, trovo che spesso il modo migliore è rinviare l’intera azione utilizzando una connessione accodata / QMetaObject :: invokeMethod (…, Qt :: QueuedConnection).

Le prossime due righe dei tuoi documenti di riferimento dicono la risposta.

Da ~ QObject ,

L’eliminazione di un QObject mentre gli eventi in attesa 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. Usa invece deleteLater (), che farà sì che il ciclo degli eventi cancelli l’object dopo che tutti gli eventi in sospeso sono stati consegnati ad esso.

In particolare ci dice di non cancellare da altri thread. Dato che hai una singola applicazione con thread, è ansible cancellare QObject .

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

Puoi trovare una risposta alla tua domanda leggendo una delle regole sugli oggetti Delta che afferma questo:

Segnale sicuro (SS).
Deve essere sicuro chiamare metodi sull’object, incluso il distruttore, all’interno di uno slot che viene chiamato da uno dei suoi segnali.

Frammento:

Al centro, QObject supporta l’eliminazione durante la segnalazione. Per approfittarne devi solo essere sicuro che il tuo object non provi ad accedere a nessuno dei suoi membri dopo essere stato cancellato. Tuttavia, la maggior parte degli oggetti Qt non sono scritti in questo modo, e non c’è nemmeno bisogno che siano entrambi. Per questo motivo, si consiglia di chiamare sempre deleteLater () se è necessario eliminare un object durante uno dei suoi segnali, poiché le probabilità sono che “cancella” causerà il crash dell’applicazione.

Sfortunatamente, non è sempre chiaro quando si dovrebbe usare ‘delete’ vs deleteLater (). Cioè, non è sempre ovvio che un percorso di codice abbia una sorgente di segnale. Spesso, potresti avere un blocco di codice che usa ‘delete’ su alcuni oggetti che è al sicuro oggi, ma ad un certo punto in futuro questo stesso blocco di codice finisce per essere invocato da una sorgente di segnale e ora improvvisamente la tua applicazione si blocca. L’unica soluzione generale a questo problema è usare deleteLater () tutto il tempo, anche se a prima vista sembra non necessario.

In genere, considero le regole degli oggetti Delta come letture obbligatorie per ogni sviluppatore Qt. È un materiale di lettura eccellente.

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

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