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 ) 的垃圾回收对开发者是不可见的,并且其运行时机也不确定,所以我们在动态创建对象时需要更加谨慎,来避免潜在可能的问题。

相关推荐
山上三树23 分钟前
Qt Widget介绍
开发语言·qt
csdn_zhangchunfeng2 小时前
Qt之slots和Q_SLOTS的区别
开发语言·qt
Larry_Yanan6 小时前
Qt网络开发之基于 QWebEngine 实现简易内嵌浏览器
linux·开发语言·网络·c++·笔记·qt·学习
一然明月8 小时前
Qt QML 锚定(Anchors)全解析
java·数据库·qt
一只爱学习的小鱼儿8 小时前
使用QT编写粒子显示热力图效果
开发语言·qt
大树学长8 小时前
【QT开发】Redis通信相关(一)
redis·qt
笨笨马甲9 小时前
Qt 人脸识别
开发语言·qt
山上三树9 小时前
Qt QObject介绍
开发语言·qt
山上三树9 小时前
QObject、QWidget、Widget三者的关系
qt
坚定学代码10 小时前
qt c++ 局域网聊天小工具
c++·qt·个人开发