2011-02-03 22 views
61

Esiste un oggetto di classe QNetworkReply. C'è uno slot (in qualche altro oggetto) collegato al suo segnale finito(). I segnali sono sincroni (quelli di default). C'è solo una discussione.In che modo delete e deleteLater funzionano per quanto riguarda i segnali e gli slot in Qt?

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

delete obj1; delete obj2; 

Ma Posso davvero? le specifiche per ~ QObject dicono:

Eliminazione di un QObject mentre gli eventi in corso sono in attesa di essere consegnati può causare un crash.

Quali sono gli "eventi in sospeso"? Potrebbe significare che mentre sto chiamando il mio delete, ci sono già alcuni "eventi in sospeso" da consegnare e che possono causare un crash e non posso davvero controllare se ce ne sono?

Quindi diciamo che io chiamo:

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

per essere sicuri.

Ma, sono davvero al sicuro? Il numero aggiunge un evento che verrà gestito nel ciclo principale quando il controllo arriva. Possono esserci alcuni eventi (segnali) in sospeso per obj1 o obj2 già lì, in attesa di essere gestiti nel ciclo principale prima del deleteLater verrà gestito? 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.

+3

Sembra 'obj-> disconnect(); obj-> deleteLater(); 'è la strada giusta da fare: – stach

risposta

59

Eliminazione QObject solito è sicuro (cioè in pratica normale, ci potrebbero essere casi patologici non sono a conoscenza di atm), se si seguono due regole fondamentali:

  • Mai eliminare un oggetto in uno slot o metodo che viene chiamato direttamente o indirettamente da un segnale (sincrono, tipo di connessione "diretto") dall'oggetto da eliminare. E.g. se si ha un'operazione di classe con un segnale Operation :: finished() e uno slot Manager :: operationFinished(), non si desidera eliminare l'oggetto 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 esempio, accedere ad un membro), e quindi operare su un "this" pointer non valido.

  • Allo stesso modo, non eliminare mai un oggetto nel codice che viene chiamato in modo sincrono dal gestore 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, come i backtrace di solito guardano strano (come incidente durante l'accesso alla variabile membro POD), soprattutto quando si hanno complicato le catene di segnale/slot in cui una delezione potrebbe verificarsi diversi passaggi verso il basso in origine avviato da un segnale o evento dall'oggetto che viene eliminato.

Tali 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'oggetto.Un altro, trovo che spesso il modo migliore è rinviare l'intera azione utilizzando una connessione accodata/QMetaObject :: invokeMethod (..., Qt :: QueuedConnection).

2

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

In caso contrario l'eliminazione di un QObject sarà prima scollegare tutti i segnali e gli slot e rimuovere tutti gli eventi in corso. Come farebbe una chiamata a disconnect().

13

Si possono trovare risposta alla vostra domanda leggere su uno dei Delta Object Rules in cui si afferma questo:

Segnale Sicuro (SS).
Deve essere sicuro per i metodi di chiamata sull'oggetto, incluso il distruttore, all'interno di uno slot chiamato da uno dei suoi segnali.

Frammento:

Al suo interno, QObject sostiene di essere cancellati mentre segnalazione. Al fine di approfittarne non resta che essere che l'oggetto non cerchi di accedere ai propri membri dopo essere cancellato. Tuttavia, la maggior parte degli oggetti Qt non sono scritti in questo modo e non è necessario che siano entrambi . Per questo motivo, si consiglia di chiamare sempre deleteLater() se è necessario eliminare un oggetto durante uno dei suoi segnali, perché è probabile che "cancella" sia arrestare l'applicazione.

Sfortunatamente, non è sempre chiaro quando si deve usare 'cancella' contro deleteLater(). Cioè, non è sempre evidente che un percorso di codice ha una sorgente di segnale . Spesso, potresti avere un blocco di codice che utilizza 'cancella' su alcuni oggetti che sono al sicuro oggi, ma ad un certo punto nel futuro questo stesso blocco di codice finisce per essere invocato da una sorgente di segnale e ora improvvisamente l'applicazione si arresta in modo anomalo. L'unica soluzione generale a questo problema è utilizzare deleteLater() tutto il tempo, anche se a prima vista sembra inutile.

In generale mi considerano Regole Object Delta lettura come obbligatorio per ogni sviluppatore Qt. È un materiale di lettura eccellente.

+1

Se segui il link a DOR, devi seguire i link su quella pagina per ulteriori letture, ad es. segui il link "Segnali sicuri". La prima pagina del link è difficile da capire senza contesto. (Sto inseguendo un arresto anomalo all'uscita usando PyQt su Windows, la mia app non sta nemmeno cancellando alcun oggetto, ma spero che il collegamento a DOR offrirà approfondimenti.) – bootchk

19

Le prossime due righe dei documenti indicati indicano la risposta.

Da ~QObject,

Eliminazione di un QObject mentre gli eventi in corso sono in attesa di essere consegnati può causare un crash. È necessario che non elimini direttamente QObject se esiste in un thread diverso da quello attualmente in esecuzione. Usa invece deleteLater(), che farà sì che il loop eventi cancelli l'oggetto dopo che tutti gli eventi in sospeso sono stati consegnati.

Precisamente ci dice di non eliminare da altri thread. Dato che hai una singola applicazione con il thread, è possibile cancellare QObject.

Altrimenti, se è necessario eliminarlo in un ambiente multi-thread, utilizzare deleteLater() che eliminerà il QObject una volta eseguita l'elaborazione di tutti gli eventi.

+5

E il mio secondo scenario? È possibile chiamare ancora gli slot di un oggetto dopo aver chiamato deleteLater su di esso? – stach