La recolección de basura QML elimina los objetos que aún están en uso

Me he encontrado con este problema en varias ocasiones, con objetos creados dinámicamente, independientemente de si se crearon en QML o C ++. Los objetos se eliminan mientras aún están en uso, lo que provoca accidentes graves sin razón aparente. Los objetos todavía están referenciados y pareados a otros objetos hasta el objeto raíz, por lo que me resulta extraño que QML elimine esos objetos mientras su recuento aún está por encima de cero.

Hasta ahora, la única solución que encontré fue crear los objetos en C ++ y establecer la propiedad en CPP explícitamente, lo que hace imposible eliminar los objetos de QML.

Al principio supuse que podría ser un problema con la crianza de los hijos, ya que estaba usandoQObject clases derivadas, y el método QML de instanciación dinámica pasa unItem para un padre, mientras queQtObject ni siquiera viene con una propiedad principal, no está expuesta deQObject.

Pero luego lo intenté con unQobject derivado que expone y usa la crianza de los hijos y finalmente incluso intentó usarItem solo por el hecho de estar seguro de que los objetos están correctamente pareados y, sin embargo, este comportamiento aún persiste.

Aquí hay un ejemplo que produce este comportamiento, desafortunadamente no pude aplanarlo a una sola fuente porque la anidación profunda deComponents lo rompe:

// 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
    }
}

El ejemplo es un generador de árbol de objetos trivial, con el botón izquierdo agregando una hoja al nodo y el botón derecho contrayendo el nodo. Todo lo que se necesita para reproducir el error es crear un nodo con una profundidad de 3 y luego colapsar y expandir el nodo raíz, sobre el cual la salida de la consola muestra:

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)

losobject del nodo más profundo se elimina sin motivo alguno, aunque esté parental al nodo principalItem y referenciado en el objeto JS en el modelo de lista. Intentar agregar un nuevo nodo al nodo más profundo bloquea el programa.

El comportamiento es consistente, independientemente de la estructura del árbol, solo el segundo nivel de nodos sobrevive, todos los nodos más profundos se pierden cuando el árbol se colapsa.

La falla no radica en el modelo de lista que se usa como almacenamiento, lo he probado con una matriz JS y unQList y los objetos todavía están perdidos. Este ejemplo utiliza un modelo de lista simplemente para guardar la implementación adicional de un modelo C ++. El único remedio que encontré hasta ahora fue negar la propiedad de QML de los objetos por completo. Aunque este ejemplo produce un comportamiento bastante consistente, en el código de producción las eliminaciones espontáneas son a menudo completamente arbitrarias.

Con respecto al recolector de basura, lo he probado antes y me di cuenta de que es bastante liberal: la creación y eliminación de objetos con un valor de 100 MB de ram no activó la recolección de basura para liberar esa memoria y, en este caso, solo unos pocos Los objetos, con un valor de unos cientos de bytes, se eliminan rápidamente.

De acuerdo con la documentación, los objetos que tienen un padre o que JS hace referencia no deben eliminarse, y en mi caso, ambos son válidos:

El objeto es propiedad de JavaScript. Cuando el objeto se devuelve a QML como el valor de retorno de una llamada al método, QML lo rastreará y lo eliminará si no hay referencias de JavaScript restantes y no tiene QObject :: parent ()

Como se menciona en la respuesta de Filip, esto no sucede si los objetos son creados por una función que no está en un objeto que se elimina, por lo que puede tener algo que ver con el estado JS vagamente mencionado asociado con los objetos QML, pero estoy esencialmente todavía en la oscuridad de por qué ocurre la eliminación, por lo que la pregunta sigue sin respuesta.

¿Alguna idea de que causa esto?

ACTUALIZAR: Nueve meses después, todavía no hay desarrollo eneste error crítico. Mientras tanto, descubrí varios escenarios adicionales donde se eliminan los objetos que todavía están en uso, escenarios en los que no importa dónde se creó el objeto y no se aplica la solución para crear simplemente los objetos en el archivo qml principal. La parte más extraña es que los objetos no se destruyen cuando se los "no hace referencia" sino que se los "vuelve a hacer referencia". Es decir, no se destruyen cuando los objetos visuales que los hacen referencia se destruyen, sino cuando se vuelven a crear.

La buena noticia es que todavía es posible establecer la propiedad en C ++ incluso para los objetos que se crean en QML, por lo que no se pierde la flexibilidad de la creación de objetos en QML. Existe el inconveniente menor de llamar a una función para proteger y eliminar cada objeto, pero al menos evita la gestión de errores de por vida de QtQuick. Sin embargo, tengo que amar la "conveniencia" de QML: ser obligado a volver amanual gestión de objetos de por vida.

Respuestas a la pregunta(1)

Su respuesta a la pregunta