c++ - 从 Q_INVOKABLE 返回的 C++ 对象由 QML 拥有和收集的规则是什么?
问题描述
我有一个 C++ 类,它在 QML 中作为产生其他类型的单例公开
qmlRegisterSingletonType<DomainManager>("my.pkg", 1, 0, "DomainManager", domain_provider);
qmlRegisterUncreatableType<Control>("my.pkg", 1, 0, "Control", "Get it fresh from DomainManager");
有DomainManager
一个功能
Q_INVOKABLE Control* controlWriter(QString partition);
根据http://doc.qt.io/qt-5/qtqml-cppintegration-data.html#data-ownership从 a 返回的对象Q_INVOKABLE
归 QML 所有,应该由 GC 删除。唯一的例外似乎是在返回的对象上设置了父级时,情况并非如此。
我有一个StackView
包含具有属性的面板:
property Control ctrl: DomainManager.controlWriter(dummy.name)
我已经验证,当我将面板从堆栈中弹出时,Component.onDestruction
会被调用,因此面板会被删除。
但是,我将以下析构函数放在 C++ 对象上,并且在整个应用程序退出之前它不会被删除。
~Control() { qDebug() << "deleting control"; };
我发现摆脱该对象的唯一方法是Control
调用手动释放它。ctrl.destroy()
Component.onDestruction
为什么 QML 不释放这个对象?
包含该属性的完整 QML 文件如下所示。ctrl
不在此文件之外使用。
import QtQuick 2.11
import my.pkg 1.0
Image {
id: dummy
property string name
property Control ctrl: DomainManager.controlWriter(dummy.name)
source: "dummy.jpg"
fillMode: Image.PreserveAspectFit
Connections {
target: gamepad
onAxisLeftYChanged: {
ctrl.id = dummy.name
ctrl.x = gamepad.axisLeftY * 32767
ctrl.yaw = gamepad.axisLeftX * 32767
ctrl.publish()
}
onAxisLeftXChanged: {
ctrl.id = dummy.name
ctrl.x = gamepad.axisLeftY * 32767
ctrl.yaw = gamepad.axisLeftX * 32767
ctrl.publish()
}
}
Component.onDestruction: {
// not sure why this requires manual clean-up
ctrl.destroy()
}
}
解决方案
让我们找出什么是 QML GC:
主文件
DomainManager *example = nullptr;
static QObject *domain_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return example;
}
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
example = new DomainManager;
qmlRegisterSingletonType<DomainManager>("my.pkg", 1, 0, "DomainManager", domain_provider);
qmlRegisterUncreatableType<Control>("my.pkg", 1, 0, "Control", "Get it fresh from DomainManager");
QQmlApplicationEngine engine;
QObject::connect(example, &DomainManager::collectGarbage,
[&engine]() {
engine.collectGarbage();
qDebug("collectGarbage");
});
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
QML
Window
{
visible: true
height: 640
width: 480
Component {
id: test
Item {
property Control ctrl: DomainManager.controlWriter("test")
}
}
Component.onCompleted: {
var c = test.createObject()
console.log("Control created")
c.destroy()
//DomainManager.collectGarbage()
console.log("Window onCompleted")
}
}
在上面的代码中,我添加了一个额外的信号collectGarbage
来DomainManager
演示 GC。
该对象归 JavaScript 所有。当对象作为方法调用的返回值返回给 QML 时,如果没有剩余的 JavaScript 对它的引用并且它没有 QObject::parent(),QML 将跟踪它并删除它。
- 上面的输出:
qml: Control created qml: Window onCompleted
- 然后取消注释
DomainManager.collectGarbage()
告诉 QML 引擎collectGarbage
. 输出将是:
qml: Control created collectGarbage qml: Window onCompleted deleting control
- 将对象的所有权更改
control
为 CppQQmlEngine::setObjectOwnership(control, QQmlEngine::CppOwnership);
输出:
qml: Control created collectGarbage qml: Window onCompleted
结论:
代码演示显示了如果我强制释放垃圾会怎样。GC不是智能指针。当对象的引用计数变为零时,QML GC 将销毁从Q_INVOKABLE
函数 except中返回的对象CppOwnership
。但不是马上。我认为它类似于 Java GC。
收集垃圾:
通常你不需要调用这个函数;当 QJSEngine 决定这样做是明智的(即当创建了一定数量的新对象时)时,垃圾收集器将自动被调用。但是,您可以调用此函数来明确请求应尽快执行垃圾收集。
推荐阅读
- javascript - 如何播放 youtube 嵌入视频的多个部分
- android - 将 LifeCycleOwner 传递给 RecyclerView.Adapter 是否安全
- elixir - Ecto - 将表列迁移到自己的连接表中(将 DATA 转移到新表中)
- javascript - Js没有响应正确的信息
- java - 在Android中显示从Databse获取的列表数据到列表视图中
- java - 正则表达式 - 从给定字符串中解析数量
- python - Django 详细视图:在详细视图中从另一个模型中检索数据
- spring-boot - javax.persistence 和 jakarta.persistence 类路径错误
- flutter - Flutter - 在另一个类中修改时获取复选框反馈
- python - 在python中找到每16行中出现频率最高的值