Как delete и deleteLater работают с сигналами и слотами в Qt? - PullRequest
72 голосов
/ 03 февраля 2011

Есть объект класса QNetworkReply. Есть слот (в каком-то другом объекте), связанный с его сигналом Finished (). Сигналы являются синхронными (по умолчанию). Есть только одна тема.

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

delete obj1; delete obj2;

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

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

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

Так скажем, я звоню:

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

На всякий случай.

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

Ответы [ 4 ]

66 голосов
/ 03 февраля 2011

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

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

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

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

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

20 голосов
/ 04 февраля 2011

В следующих двух строках указанных вами документов указан ответ.

От ~ QObject ,

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

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

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

13 голосов
/ 03 февраля 2011

Вы можете найти ответ на свой вопрос, прочитав одно из Дельта-правил объекта , в котором говорится:

Сигнальный сейф (SS).
Это должно быть безопасно вызов методов на объекте, в том числе деструктор, изнутри щели вызывается одним из его сигналов.

Фрагмент:

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

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

Обычно я считаю Delta Object Rules обязательным для прочтения для каждого разработчика Qt. Это отличный материал для чтения.

2 голосов
/ 03 февраля 2011

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

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

...