簡體   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