QML垃圾收集删除仍在使用的对象

dte*_*ech 9 javascript qt garbage-collection qml qtquick2

我曾多次遇到这个问题,动态创建对象,无论它们是用QML还是C++创建的.在仍在使用时删除对象,导致硬崩溃,没有明显原因.这些对象仍然被引用并且是其他对象的一部分,一直到根对象,所以我发现QML在它们的引用数仍然高于零时删除这些对象是很奇怪的.

到目前为止,我找到的唯一解决方案是使用C++创建对象并将所有权明确设置为CPP,从而无法从QML中删除对象.

起初我认为它可能是一个父母问题,因为我使用QObject派生类,动态实例化的QML方法传递Item一个父类,而QtObject甚至没有父属性 - 它没有公开QObject.

但后来我尝试了一个Qobject派生的暴露和使用育儿,最后甚至尝试使用Item只是为了确保对象是正确的父对象,但这种行为仍然存在.

这是一个产生这种行为的例子,遗憾的是我无法将其压平为单个源,因为Components 的深层嵌套会破坏它:

// ObjMain.qml
Item {
    property ListModel list : ListModel { }
    Component.onCompleted: console.log("created " + this + " with parent " + parent)
    Component.onDestruction: console.log("deleted " + this)
}

// Uimain.qml
Item {
    id: main
    width: childrenRect.width
    height: childrenRect.height
    property Item object
    property bool expanded : true
    Loader {
        id: li
        x: 50
        y: 50
        active: expanded && object && object.list.count
        width: childrenRect.width
        height: childrenRect.height
        sourceComponent: listView
    }
    Component {
        id: listView
        ListView {
            width: contentItem.childrenRect.width
            height: contentItem.childrenRect.height
            model: object.list
            delegate: Item {
                id: p
                width: childrenRect.width
                height: childrenRect.height
                Component.onCompleted: Qt.createComponent("Uimain.qml").createObject(p, {"object" : o})
            }
        }
    }
    Rectangle {
        width: 50
        height: 50
        color: "red"

        MouseArea {
            anchors.fill: parent
            acceptedButtons: Qt.RightButton | Qt.LeftButton
            onClicked: {
                if (mouse.button == Qt.RightButton) {
                    expanded = !expanded
                } else {
                    object.list.append({ "o" : Qt.createComponent("ObjMain.qml").createObject(object) })
                }
            }
        }
    }
}

// main.qml

Window {
    visible: true
    width: 1280
    height: 720

    ObjMain {
        id: obj
    }

    Uimain {
        object: obj
    }
}
Run Code Online (Sandbox Code Playgroud)

该示例是一个简单的对象树构建器,左侧按钮向节点添加叶子,右侧按钮折叠节点.重现错误所需的只是创建深度为3的节点,然后折叠并展开根节点,控制台输出显示在该节点上:

qml: created ObjMain_QMLTYPE_0(0x1e15bb8) with parent QQuickRootItem(0x1e15ca8)
qml: created ObjMain_QMLTYPE_0(0x1e5afc8) with parent ObjMain_QMLTYPE_0(0x1e15bb8)
qml: created ObjMain_QMLTYPE_0(0x1e30f58) with parent ObjMain_QMLTYPE_0(0x1e5afc8)
qml: deleted ObjMain_QMLTYPE_0(0x1e30f58)
Run Code Online (Sandbox Code Playgroud)

object最深的节点被删除没有理由,即使它是父父节点Item,并在列表模型中的JS对象引用.尝试将新节点添加到最深节点会导致程序崩溃.

行为是一致的,无论树的结构如何,只有第二级节点存活,所有更深的节点在树折叠时丢失.

故障不在于用作存储的列表模型,我已经使用JS数组和a进行了测试,QList并且对象仍然丢失.此示例仅使用列表模型来保存C++模型的额外实现.到目前为止,我发现的唯一补救措施是完全拒绝QML对象的所有权.虽然这个例子产生了相当一致的行为,但在生产代码中,自发删除通常是完全随意的.

关于垃圾收集器 - 我以前测试过它,并注意到它非常自由 - 创建和删除100 MB内存的对象没有触发垃圾收集来释放内存,但在这种情况下只有少数值得几百字节的对象被匆匆删除.

根据文档,不应删除具有父项或由JS引用的对象,在我的情况下,两者都是有效的:

该对象归JavaScript所有.当对象作为方法调用的返回值返回到QML时,QML将跟踪它并删除它,如果没有剩余的JavaScript引用它并且它没有QObject :: parent()

正如Filip的回答中提到的,如果对象是由不在被删除的对象中的函数创建的,那么这不会发生,因此它可能与模糊提到的与QML对象关联的JS状态有关,但我本质上是仍然在黑暗中,为什么删除发生,所以这个问题实际上仍然没有答案.

有什么想法导致这个?

更新:九个月后,这个关键错误仍然没有发展.同时我发现了一些其他场景,其中仍在使用的对象被删除,在哪里创建对象无关紧要的场景以及简单地在主qml文件中创建对象的解决方法不适用.最奇怪的部分是当对象被"未引用"时它们没有被破坏,但是它们被"重新引用".也就是说,当引用它们的可视对象被破坏时,它们不会被破坏,但是当它们被重新创建时.

好消息是,即使对于使用QML创建的对象,仍然可以将所有权设置为C++,因此QML中对象创建的灵活性不会丢失.调用函数来保护和删除每个对象有一点不便,但至少可以避免QtQuick的错误生命周期管理.一定要喜欢QML的"便利性" - 被迫回到手动对象生命周期管理.

Fil*_*ski 0

从管理内存的方式来看,QML 与 C++ 不同。QML 旨在处理内存分配和释放。我认为你发现的问题就是这样的结果。

如果动态对象创建太深入,所有内容似乎都会被删除。因此,您创建的对象是数据的一部分并不重要 - 它们也会被销毁。

不幸的是我的知识到此为止。

解决该问题的方法之一(证明我之前的陈述)是将数据结构的创建从动态 UI qml 文件中移出:

  1. 将对象创建函数放置在main.qml中

function createNewObject(parentObject) {
    parentObject.list.append({ "o" : Qt.createComponent("ObjMain.qml").createObject(parentObject) })
}
Run Code Online (Sandbox Code Playgroud)
  1. 在您的代码中使用此函数:

// fragment of the Uimain.qml file
    MouseArea {
        anchors.fill: parent
        acceptedButtons: Qt.RightButton | Qt.LeftButton
        onClicked: {
            if (mouse.button == Qt.RightButton) {
                expanded = !expanded
            } else {
                createNewObject(object)
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)