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()
}
}
}
基于 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()
}
}
}