QObject::deleteLater не вызывается, как ожидалось в моем тесте Qt
Я тестирую общую библиотеку, содержащую вызов deleteLater
внутренне. Внутри библиотеки не выполняется цикл обработки событий, поэтому для приложения необходимо, чтобы цикл обработки событий выполнялся, чтобы вся память была правильно освобождена.
Но в тесте объект dtor вызывается не так, как ожидалось.
Например:
void test1()
{
Foo foo;
QSignalSpy spy(&foo, SIGNAL(mySignal(Status)));
foo.do(); // should trigger mySignal
QVERIFY(spy.wait(10000)); // event loop started for 10 s max
QCOMPARE(spy.count(), 1);
QList<QVariant> sig = spy.takeFirst();
Foo::Status status = qvariant_cast<Foo::Status>(sig.at(0));
QVERIFY2(status == Foo:Ok, "Failed");
}
Класс Foo
выглядит так:
class Foo : public QObject
{
Q_OBJECT
// ... methods, signals, slots..
private slots:
// this call is asynchronous (depends on a network reply)
void myslot() {
//..
m_obj->deleteLater();
emit mySignal(Foo:Ok);
}
};
Я добавил отладочную печать в dtor m_obj, и он не вызывается при выполнении test1.
Однако, если я выполню тест дважды (добавив слот test2, который является копией test1), он вызывается один раз.
Насколько я понимаю, когда сигнал испускается, он останавливает цикл шпионских событий, а затем deleteLater
никогда не называется. И после этого второй цикл обработки событий запускается в test2, он обрабатывает ожидающее удаление из предыдущего test1.
Это правильно? Спасибо.
1 ответ
Да, ты прав. Так как QSignalSpy
а также Foo
живи в одной ветке, сигнал mySignal
доставляется не через цикл обработки событий, а через прямое соединение. Таким образом, цикл обработки событий останавливается сразу после myslot
, Тем не менее, так как myslot
был вызван тем же циклом событий, управление возвращается к нему только тогда, когда myslot
возвращается. Таким образом, к тому времени, когда цикл обработки событий потенциально может выполнить очистку, запрошенную deleteLater
это уже было остановлено.
Если вы хотите проверить это m_obj
очищен правильно, вы могли бы вместо этого создать дополнительный QSignalSpy
и подключите его к QObject::destroyed
сигнал, что каждый QObject
испускает, когда это разрушено.
Вам, однако, нужно пройти в m_obj
как зависимость от Foo
либо в конструкторе, либо через сеттер вместо того, чтобы создавать его в Foo
сам.
Тест может выглядеть примерно так:
void test1()
{
auto obj = new Obj{}; // will be assigned to `Foo::m_obj`
Foo foo{obj};
QSignalSpy deletion_spy(obj, &QObject::destroyed);
QSignalSpy mysignal_spy(&Foo, &Foo::mySignal);
QVERIFY(deletion_spy.wait(10000));
QCOMPARE(deletion_spy.count(), 1); // verify that `obj` got deleted
QCOMPARE(mysignal_spy.count(), 1);
QList<QVariant> sig = spy.takeFirst();
Foo::Status status = qvariant_cast<Foo::Status>(sig.at(0));
QVERIFY2(status == Foo:Ok, "Failed");
}