我在多个场合遇到过这个问题,对象是动态创建的,无论它们是用 QML 还是 C++ 创建的。对象在仍在使用时被删除,导致无明显原因的硬崩溃。这些对象一直被引用并成为其他对象的父对象,一直到根对象,所以我发现 QML 在它们的引用计数仍然高于零时删除这些对象很奇怪。
到目前为止,我找到的唯一解决方案是在 C++ 中创建对象并将所有权显式设置为 CPP,从而无法从 QML 中删除对象。
起初我认为这可能是育儿方面的问题,因为我使用的是QObject
派生类,并且动态实例化的 QML 方法Item
为父级传递了 an ,而QtObject
它甚至没有带有父级属性——它没有从QObject
.
但是后来我尝试了一个Qobject
暴露和使用Item
父级的派生,最后甚至尝试使用只是为了确保对象是正确的父级,但这种行为仍然存在。
这是一个产生这种行为的例子,不幸的是我无法将它扁平化为单一来源,因为Component
s的深层嵌套破坏了它:
// 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
}
}
该示例是一个简单的对象树构建器,左侧按钮向节点添加叶子,右侧按钮折叠节点。重现该错误所需要的只是创建一个深度为 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)
在object
最深的节点被删除没有理由,即使它是父父节点Item
,并在列表模型中的JS对象引用。尝试向最深节点添加新节点会使程序崩溃。
行为是一致的,无论树的结构如何,只有第二层节点存活,当树折叠时所有更深的节点都丢失。
错误不在于用作存储的列表模型,我已经用 JS 数组和 a 进行了测试,QList
但对象仍然丢失。此示例仅使用列表模型来保存 C++ 模型的额外实现。到目前为止,我发现的唯一补救措施是完全拒绝 QML 对对象的所有权。虽然这个例子产生了相当一致的行为,但在生产代码中,自发删除通常是完全任意的。
关于垃圾收集器 - 我之前测试过它,并注意到它非常自由 - 创建和删除 100 MB 内存的对象并没有触发垃圾收集以释放该内存,但在这种情况下只有少数value几百字节的对象被仓促删除。
根据文档,不应删除具有父对象或被 JS 引用的对象,在我的情况下,两者都是有效的:
该对象归 JavaScript 所有。当对象作为方法调用的返回值返回给 QML 时,如果没有剩余的 JavaScript 引用并且没有 QObject::parent(),QML 将跟踪它并删除它
正如 Filip 的回答中提到的,如果对象是由不在被删除的对象中的函数创建的,则不会发生这种情况,因此它可能与与 QML 对象关联的含糊提及的 JS 状态有关,但我本质上是至于为什么会发生删除,仍然一无所知,所以这个问题实际上仍然没有答案。
任何想法是什么原因造成的?
更新:九个月后,这个关键错误的开发仍然为零。同时,我发现了几个额外的场景,其中仍在使用的对象被删除,对象的创建位置无关紧要的场景以及简单地在主 qml 文件中创建对象的解决方法不适用。最奇怪的部分是对象在“未引用”时没有被销毁,而是在“重新引用”时被销毁。也就是说,当引用它们的视觉对象被销毁时,它们并没有被销毁,而是在它们被重新创建时被销毁。
好消息是,即使对于在 QML 中创建的对象,仍然可以将所有权设置为 C++,因此不会失去在 QML 中创建对象的灵活性。调用函数来保护和删除每个对象会带来一些不便,但至少可以避免 QtQuick 有问题的生命周期管理。不得不喜欢 QML 的“便利性”——被迫回到手动对象生命周期管理。