QtQuick 的 ListView 无法获得 QAbstractItemModel 对象的所有权

QtQuick's ListView fails to take ownership of QAbstractItemModel object

基于 Qt documentation,每当 QObject 指针类型通过 Q_INVOKABLE[ 从 C++ 代码传递到 QML 时=39=] 方法,有一组规则确定谁负责该指针的生命周期。如果 QObject 是无父对象,QML 引擎隐含地负责获取指针的所有权。

在我的场景中,我希望我的前端 UI 代表后端 C++ 代码 generated/provided 的列表模型。我的假设是,只要 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()
        }
    }
}

这是程序截图:

用户点击按钮的那一刻,垃圾收集器运行并销毁模型指针(stdout 中的 "Creation" 和 "Destruction" 输出表明销毁)。

我很好奇指针为什么会被破坏?我注意到它没有将 ListView 设置为它的父级,这很公平,我认为 QML 引擎会使用某种形式的引用指针来尝试跟踪谁仍然持有它的参考。是否有文档可以更深入地了解垃圾收集/所有权的实现方式。

同样,是否有更好的方法来构建此代码,同时仍然满足将无父对象 QObject 传递回 QML 的要求。

销毁的原因似乎是因为 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()
        }
    }
}