Сборка мусора QML удаляет все еще используемые объекты
Я сталкивался с этой проблемой несколько раз, когда объекты создавались динамически, независимо от того, были ли они созданы в QML или C ++. Объекты удаляются во время использования, что приводит к серьезным сбоям без видимой причины. На объекты по-прежнему ссылаются и парентируют с другими объектами вплоть до корневого объекта, поэтому я нахожу странным, что QML удаляет эти объекты, пока их счет пересчета все еще находится выше нуля.
До сих пор единственным решением, которое я нашел, было создание объектов в C ++ и явное указание владельца на CPP, что делало невозможным удаление объектов из QML.
Сначала я предположил, что это может быть проблема с воспитанием детей, так как я использовалQObject
производные классы, а метод динамического создания QML передаетItem
для родителя, тогда как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 иQList
и объекты все еще потеряны. В этом примере модель списка используется только для сохранения дополнительной реализации модели C ++. Единственное средство, которое я нашел до сих пор, было полностью отрицать владение объектами QML. Хотя этот пример демонстрирует довольно непротиворечивое поведение, в производственном коде спонтанные удаления часто бывают совершенно произвольными.
Что касается сборщика мусора - я тестировал его раньше и заметил, что он довольно либеральный - создание и удаление объектов стоимостью 100 МБ оперативной памяти не запускает сборку мусора для освобождения этой памяти, и все же в этом случае только несколько объекты размером в несколько сотен байт спешно удаляются.
Согласно документации, объекты, у которых есть родительский объект или на которые ссылается JS, не должны быть удалены, и в моем случае оба действительны:
Объект принадлежит JavaScript. Когда объект возвращается в QML как возвращаемое значение вызова метода, QML отслеживает его и удаляет, если на него нет оставшихся ссылок JavaScript, и у него нет QObject :: parent ()
Как упоминалось в ответе Филипа, этого не происходит, если объекты создаются функцией, которая не находится в удаляемом объекте, поэтому она может иметь какое-то отношение к смутно упомянутому состоянию JS, связанному с объектами QML, но я по сути все еще в неведении относительно того, почему удаление происходит, таким образом, вопрос фактически все еще остается без ответа.
Есть идеи, что вызывает это?
ОБНОВИТЬ: Девять месяцев спустя все еще ноль развитияэта критическая ошибка, Между тем я обнаружил несколько дополнительных сценариев, в которых объекты, которые все еще используются, удаляются, сценарии, в которых не имеет значения, где был создан объект, и обходной путь для простого создания объектов в основном файле qml не применяется. Самая странная часть в том, что объекты не уничтожаются, когда на них не ссылаются, а когда на них ссылаются. То есть они уничтожаются не тогда, когда визуальные объекты, на которые они ссылаются, разрушаются, а когда они воссоздаются.
Хорошей новостью является то, что все еще можно установить владение C ++ даже для объектов, которые создаются в QML, поэтому гибкость создания объектов в QML не теряется. Существует небольшое неудобство при вызове функции для защиты и удаления каждого объекта, но, по крайней мере, вы избегаете ошибочного управления временем жизни QtQuick. Должен любить "удобство" QML, хотя - вынужден вернуться круководство Управление жизненным циклом объекта.