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的"便利性" - 被迫回到手动对象生命周期管理.
从管理内存的方式来看,QML 与 C++ 不同。QML 旨在处理内存分配和释放。我认为你发现的问题就是这样的结果。
如果动态对象创建太深入,所有内容似乎都会被删除。因此,您创建的对象是数据的一部分并不重要 - 它们也会被销毁。
不幸的是我的知识到此为止。
解决该问题的方法之一(证明我之前的陈述)是将数据结构的创建从动态 UI qml 文件中移出:
function createNewObject(parentObject) {
parentObject.list.append({ "o" : Qt.createComponent("ObjMain.qml").createObject(parentObject) })
}
Run Code Online (Sandbox Code Playgroud)
// 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)