繁体   English   中英

从Q_INVOKABLE返回的C ++对象由QML拥有和收集的规则是什么?

[英]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将对其进行跟踪并将其删除。

  1. 上面的输出:
 qml: Control created qml: Window onCompleted 
  1. 然后取消注释DomainManager.collectGarbage()告诉QML引擎collectGarbage 输出将是:
 qml: Control created collectGarbage qml: Window onCompleted deleting control 
  1. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM