2011-02-03 6 views
61

Существует объект класса QNetworkReply. Существует слот (в каком-то другом объекте), подключенный к его завершенному() сигналу. Сигналы синхронны (по умолчанию). Существует только один поток.Как delete и deleteLater работает в отношении сигналов и слотов в Qt?

В какой-то момент времени я хочу избавиться от обоих объектов. Больше никаких сигналов или чего-либо от них. Я хочу, чтобы они ушли. Ну, я думал, я буду использовать

delete obj1; delete obj2; 

Но могу ли я на самом деле? Спецификации для ~ QObject сказать:

Удаление QObject в то время как в ожидании событий, ожидающих быть доставлены может привести к аварии.

Что такое «ожидающие события»? Может ли это означать, что, когда я звоню в мой delete, уже есть какие-то «ожидающие события», которые могут быть доставлены, и что они могут вызвать сбой, и я не могу проверить, есть ли они?

Итак, давайте говорить, что я называю:

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

Чтобы быть в безопасности.

Но я действительно в безопасности? В deleteLater добавляется событие, которое будет обрабатываться в основном цикле, когда элемент управления попадает туда. Могут ли быть ожидающие события (сигналы) для obj1 или obj2 уже там, ожидающие обработки в основном контуре до deleteLater будет обработан? Это было бы очень неудачно. Я не хочу писать проверку кода для статуса «несколько удаленных» и игнорирования входящего сигнала во всех моих слотах.

+3

Похож на 'obj-> disconnect(); obj-> deleteLater(); 'правильный путь: – stach

ответ

59

Удаление объектов QObject обычно безопасна (т.е. в обычной практике, там могут быть патологические случаи, я не в курсе атм), если следовать двум основным правилам:

  • Никогда не удаляйте объект в слоте или метод, который прямо или косвенно вызван сигналом (синхронным, тип соединения «прямой») от объекта, подлежащего удалению. . если у вас есть класс Operation with signal Operation :: finished() и slot Manager :: operationFinished(), вы не хотите удалять объект операции, который излучал сигнал в этом слоте. Метод, испускающий законченный() сигнал, может продолжать доступ к «этому» после испускания (например, к доступу к члену), а затем работать с недействительным «этим» указателем.

  • Аналогичным образом никогда не удаляйте объект в коде, который синхронно вызывается из обработчика события объекта. Например. не удаляйте SomeWidget в его SomeWidget :: fooEvent() или в методах/слотах, которые вы вызываете оттуда. Система событий продолжит работу с уже удаленным объектом -> Crash.

И может быть сложно отследить, как Цепочки вызовов обычно выглядят странно (как сбой при обращении к переменной-члена POD), особенно, когда вы усложнили сигнал/слот цепи, где удаление может происходить несколько шагов вниз первоначально инициированный сигналом или событием из объекта, который удален.

Такие случаи являются наиболее распространенным вариантом использования deleteLater(). Он гарантирует, что текущее событие может быть выполнено до того, как элемент управления вернется в цикл событий, который затем удалит объект.Другой, я считаю, что лучший способ - отложить все действие с помощью очередного соединения/QMetaObject :: invokeMethod (..., Qt :: QueuedConnection).

2

Насколько я знаю, это в основном проблема, если объекты существуют в разных потоках. Или, может быть, пока вы на самом деле обрабатываете сигналы.

В противном случае удаление QObject сначала отключит все сигналы и слоты и удалит все ожидающие события события. Как вызов disconnect().

13

Вы можете найти ответ на свой вопрос, читая об одном из Delta Object Rules, которая гласит так:

сигнала Safe (SS).
Должно быть безопасно использовать методы вызова на объекте, включая деструктор, изнутри слота , вызываемого одним из его сигналов.

Фрагмент:

По своей сути, QObject поддерживает быть удален во время передачи сигналов. Для того, чтобы воспользовался этим, вам нужно только быть уверенным, что ваш объект не пытается получить доступ к любому из своих элементов после удаления . Однако большинство объектов Qt не записываются таким образом, и не требуется для них быть . По этой причине рекомендовал, чтобы вы всегда вызывали deleteLater(), если вам нужно удалить объект во время одного из его сигналов, , потому что коэффициенты «delete» будут просто сбой приложения.

К сожалению, не всегда ясно , когда вы должны использовать 'delete' vs deleteLater(). То есть, это не всегда очевидно, что путь кода имеет источник сигнала . Часто у вас может быть блок кода , который использует 'delete' на некоторые объекты, которые сегодня безопасны, но в какой-то момент в будущем этот же блок кода заканчивается тем, что вызывается из источника сигнала и теперь внезапно Ваше приложение рушится. Единственное общее решение этой проблемы - использовать deleteLater() все время, даже , если с первого взгляда кажется ненужным.

Вообще я считаю Delta Правила объекта, как обязательное чтение для каждого разработчика Qt. Это отличный материал для чтения.

+1

Если вы переходите по ссылке на DOR, вы должны следовать ссылкам на этой странице для дальнейшего чтения, например. перейдите по ссылке «Сигнальный сейф». Первая страница из ссылки трудно понять без контекста. (Я преследую крах при выходе с использованием PyQt в Windows, мое приложение даже не удаляет какие-либо объекты, но я надеюсь, что ссылка на DOR предложит идеи.) – bootchk

19

Следующие две строки ваших упомянутых документов говорят ответ.

~QObject С,

Удаление QObject в то время как в ожидании событий, ожидающих быть доставлены может привести к аварии. Вы не должны удалять QObject напрямую, если он существует в другом потоке, чем тот, который выполняется в настоящий момент. Вместо этого используйте deleteLater(), из-за чего цикл цикла будет удалять объект после отправки всех ожидающих событий.

В нем конкретно говорится, что мы не должны удалять другие темы. Поскольку у вас есть однопоточное приложение, можно удалить QObject.

Иначе, если вам нужно удалить его в многопоточной среде, используйте deleteLater(), который удалит ваш QObject после того, как обработка всех событий будет выполнена.

+5

Как насчет моего второго сценария? Можно ли вызывать слоты в объекте после вызова функции deleteLater? – stach