[英]What are the rules for a C++ object returned from a Q_INVOKABLE to be owned and collected by QML?
我有一個在QML中公開為生成其他類型的單例的C ++類
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 ,從Q_INVOKABLE
返回的對象歸QML所有,應由GC刪除。 唯一的例外似乎是在返回的對象上設置了父對象時,情況並非如此。
我有一個StackView
,其中包含具有屬性的面板:
property Control ctrl: DomainManager.controlWriter(dummy.name)
我已經驗證了,當我將面板彈出堆棧時,會調用Component.onDestruction
,因此該面板確實會被刪除。
但是,我將以下析構函數放在C ++對象上,直到整個應用程序退出,它都不會被刪除。
~Control() { qDebug() << "deleting control"; };
我發現擺脫Control
對象的唯一方法是在Component.onDestruction
調用ctrl.destroy()
以手動釋放它。
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:
main.cpp中
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")
}
}
在上面的代碼中,我添加了DomainManager
的附加信號collectGarbage
來演示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
對象的所有權更改為Cpp QQmlEngine::setObjectOwnership(control, QQmlEngine::CppOwnership);
輸出: qml: Control created collectGarbage qml: Window onCompleted
結論:
該代碼演示顯示了如果我強制釋放垃圾該怎么辦。 GC 不是智能指針。 當對象的引用計數變為零時,QML GC將銷毀從Q_INVOKABLE
函數返回的對象( CppOwnership
除外)。 但不是立即。 我認為它類似於Java GC。
collectGarbage:
通常,您不需要調用此函數。 當QJSEngine決定這樣做時,垃圾收集器將被自動調用(即,當創建了一定數量的新對象時)。 但是,您可以調用此函數來顯式請求應盡快執行垃圾回收。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.