ListView QtQuick не может стать владельцем объекта QAbstractItemModel

На основе документации Qt всякий раз, когда тип указателя QObject передается из кода C++ в QML посредством метода Q_INVOKABLE, существует набор правил, определяющих, кто отвечает за время жизни этого указателя. Если объект QObject не имеет родителей, то неявно механизм QML отвечает за принятие владения указателем.

В моем сценарии я хочу, чтобы мой интерфейс пользователя представлял модель списка, которая генерируется / предоставляется внутренним кодом C++. Мое предположение состоит в том, что указатель будет оставаться в живых до тех пор, пока на него ссылается код QML. Код ниже показывает урезанный тестовый пример:

main.cpp

#include <QAbstractItemModel>
#include <QDebug>
#include <QGuiApplication>
#include <QObject>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QStringListModel>

class MyStringListModel : public QStringListModel
{
    Q_OBJECT

public:

    explicit MyStringListModel(const QStringList &strings, QObject* parent=nullptr) : QStringListModel(strings, parent)
    {
        qDebug() << "Creation";
    }

    virtual ~MyStringListModel() override
    {
        qDebug() << "Destruction";
    }
};

class Backend : public QObject
{
    Q_OBJECT

public:

    Backend(QObject* parent=nullptr) : QObject(parent)
    {

    }

    Q_INVOKABLE QAbstractItemModel* createModel() const
    {
        static const QStringList months = {
            tr("January"),
            tr("February"),
            tr("March"),
            tr("April"),
            tr("May"),
            tr("June"),
            tr("July"),
            tr("August"),
            tr("September"),
            tr("October"),
            tr("November"),
            tr("December"),
        };

        return new MyStringListModel(months);
    }
};

int main(int argc, char* argv[])
{
    QGuiApplication application(argc, argv);

    qmlRegisterType<QAbstractItemModel>();

    Backend backend;

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("backend", &backend);
    engine.load("qrc:///ui/main.qml");

    return application.exec();
}

#include "main.moc"

Main.qml

import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.1

ApplicationWindow {
    id: window

    width: 200
    height: 250
    visible: true

    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 10

        ListView {

            Layout.fillWidth: true
            Layout.fillHeight: true

            model: backend.createModel()
            delegate: Text {
                anchors.horizontalCenter: parent.horizontalCenter
                text: model.display
            }
        }

        Button {
            Layout.alignment: Qt.AlignCenter
            text: qsTr("Garbage Collect")
            onClicked: gc()
        }
    }
}

Это скриншот программы:

В тот момент, когда пользователь нажимает на кнопку, сборщик мусора запускается и уничтожает ptr модели (разрушение видно по выводу "Создание" и "Уничтожение" в stdout).

Мне любопытно узнать, почему указатель был уничтожен? Я заметил, что он не установил ListView в качестве своего родителя, что достаточно справедливо, я подумал, что механизм QML использовал бы некоторую форму указателя ссылки, чтобы попытаться отследить, кто все еще хранит ссылку на него. Есть ли документ, который дает более полное представление о том, как осуществляется сборка мусора / владение.

Аналогичным образом, существует ли лучший способ структурирования этого кода при одновременном удовлетворении требований передачи QObject без родителей обратно в QML.

1 ответ

Решение

Кажется, что причина уничтожения заключается в том, что на объект не ссылаются в QML, например, если он назначен свойству, сборщик мусора не повлияет на него:

ApplicationWindow {
    id: window
    width: 200
    height: 250
    visible: true
    property var mymodel: backend.createModel()
    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 10
        ListView {
            Layout.fillWidth: true
            Layout.fillHeight: true
            model: mymodel
            delegate: Text {
                anchors.horizontalCenter: parent.horizontalCenter
                text: display
            }
        }
        Button {
            Layout.alignment: Qt.AlignCenter
            text: qsTr("Garbage Collect")
            onClicked: gc()
        }
    }
}
Другие вопросы по тегам