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");
}
Другие вопросы по тегам