Сборка мусора QML удаляет все еще используемые объекты

Я сталкивался с этой проблемой несколько раз, когда объекты создавались динамически, независимо от того, были ли они созданы в 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
    }
}

Примером является построитель дерева тривиальных объектов: левая кнопка добавляет лист к узлу, а правая кнопка сворачивает узел. Все, что нужно для воспроизведения ошибки, - это создать узел с глубиной 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, хотя - вынужден вернуться круководство Управление жизненным циклом объекта.

Ответы на вопрос(2)

Ваш ответ на вопрос