Qml 中的那些坑(六)---对象被错误删除,看不见的垃圾回收

【写在前面】

在 Qml 中,很多时候我们需要动态创建一些 Qml 对象,通常是:createComponent + createObjectcreateQmlObject

然而,最近工作中却出现了一个相当难以察觉的问题:动态创建的窗口在某些时刻会被莫名其妙的删除,我花了很多时间才定位到关键位置。

其根本原因在于:未给动态创建的对象分配 parent ( 即:没有任何对象持有其引用 ),结果就是,当 Qml 引擎运行垃圾回收时,这些对象会被错误清除掉。


【正文开始】

这里先直接上代码来看清我的意图,main.qml:

javascript 复制代码
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15

Window {
    id: root
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    color: "#a00000"

    Row {
        Button {
            text: "创建"
            onClicked: {
                let component = Qt.createComponent("TestWindow.qml");
                if (component.status === Component.Ready) {
                    component.createObject();
                }
            }
        }

        Button {
            text: "回收"
            onClicked: {
                qmlApi.collectGarbage();
            }
        }
    }
}

可以看到,创建按钮动态创建了一个窗口,而回收按钮则直接运行 Qml 的垃圾收集器

这部分需要借助 C++ 来运行:

c++ 复制代码
class QmlApi : public QObject
{
    Q_OBJECT

public:
    QmlApi(QQmlEngine *engine, QObject *parent = nullptr)
        : QObject(parent)
        , m_engine(engine)
    {

    }

    Q_INVOKABLE void collectGarbage() {
        if (m_engine)
            m_engine->collectGarbage();
    }

    QQmlEngine *m_engine = nullptr;
};

TestWindow.qml 则只是一个简单的窗口,有一个活动的矩形:

javascript 复制代码
import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Test Window")

    Rectangle {
        width: 100
        height: 100
        anchors.centerIn: parent
        color: "green"

        RotationAnimation on rotation {
            loops: Animation.Infinite
            from: 0
            to: 360
        }
    }
}

运行代码可以看到,一旦我们运行垃圾收集器,刚刚创建出的窗口全部都会被清除:

很明显,这根本不是我们的本意,究其原因,是创建的窗口不可访问:

垃圾收集器将尝试通过定位和处理脚本环境中不再可访问的对象来回收内存。 通常情况下,您不需要调用此函数;当 QJSEngine 决定这样做是明智的时(即,当创建了一定数量的新对象时),垃圾收集器将自动被调用。但是,您可以调用此函数来明确请求应尽快执行垃圾收集。

因此,正确的解决方法是:持有创建后的对象。

例如可以使用变量存储,又或者给这些窗口一个 parent ( 隐式持有 )。


【结语】

实际上,由于 JS( Qml ) 的垃圾回收对开发者是不可见的,并且其运行时机也不确定,所以我们在动态创建对象时需要更加谨慎,来避免潜在可能的问题。

相关推荐
上去我就QWER14 小时前
Qt中的QShortcut:高效键盘快捷方式开发指南
开发语言·c++·qt
寻找华年的锦瑟19 小时前
Qt-视频播放器
开发语言·qt
IT阳晨。1 天前
【QT开发】交叉编译QT程序在ARMLinux平台上运行
c++·qt·交叉编译·armlinux·代码移植
byxdaz1 天前
Qt中日期/时间/时区类 (QDate, QTime, QDateTime,QTimeZone)
qt·时间·时区
紫荆鱼1 天前
PCL实战项目-软件界面搭建RibbonUI
qt·pcl·用户界面·qml·点云处理
2739920291 天前
qInstallMessageHandler(重定向至log文件)
开发语言·qt
火山上的企鹅2 天前
Qt C++ 软件开发工程师面试题
c++·qt·面试
Industio_触觉智能2 天前
开源鸿蒙SIG-Qt技术沙龙成都站成功举办,产品方案展示
qt·harmonyos·openharmony·开源鸿蒙·sig-qt
上去我就QWER2 天前
深入解析Qt中的QDrag:实现灵活的拖放交互
c++·qt
奔跑吧 android2 天前
【瑞芯微】【rk3128】【移植 qt 5.12.9】
qt·arm·瑞芯微·rk3128