Qt5.6 QML,为什么动态模型在垃圾收集后被破坏?

时间:2016-05-19 14:08:54

标签: qt qml qt5 qt5.6

我有不同数量的组件,所以我试图给每个组件自己model。在这个例子中,我只创建一个,但想法是一样的。

GC()有点随机,因此在示例中,我点击了强制 gc()后点击以清除问题。会发生什么是model被破坏并变为空。之后,click方法无法使用它。

main.qml:

import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.2

import com.example.qml 1.0

ApplicationWindow
{
    visible: true
    width: 640
    height: 480

    // builder of dynamic models
    ModelFactory { id: maker }

    Column
    {
        anchors.fill: parent
        Repeater
        {
            // create dynamic model
            model: maker.makeModel();
            delegate: Label
            {
                id: label
                text: model.name

                MouseArea
                {
                    anchors.fill: parent
                    onClicked:
                    {
                        // works once until gc()
                        console.log("clicked on " + model.name)

                        // wont work anymore. model is destroyed
                        gc();
                    }
                }
            }

        }
    }
}

C ++ / mymodel.h:

#include <QAbstractListModel>
#include <QQmlApplicationEngine>
#include <QObject>
#include <QString>
#include <QDebug>

class BoxModel : public QAbstractListModel
{
    Q_OBJECT

public:

    ~BoxModel()
    {
        // see that it does get destroyed
        qDebug() << "~BoxModel()";
    }

    int rowCount(const QModelIndex& parent = QModelIndex()) const override
    {
        return 5;
    }  

    QVariant data(const QModelIndex &index, int role) const override
    {
        int ix = index.row();
        if (ix < 1) return "Larry";
        if (ix < 2) return "Barry";
        if (ix < 3) return "Gary";
        if (ix < 4) return "Harry";
        return "Sally";
    }

    QHash<int, QByteArray> roleNames() const override
    {
        QHash<int, QByteArray> roles;
        roles[Qt::UserRole+1] = "name";
        return roles;
    }

};

class ModelFactory: public QObject
{
    Q_OBJECT

public:

    Q_INVOKABLE BoxModel* makeModel()
    {
        return new BoxModel();
    }    
};

main.cpp只注册类型:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <qqmlcontext.h>
#include <qqml.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
#include "mymodel.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;

    qmlRegisterType<BoxModel>("com.example.qml", 1, 0, "BoxModel");
    qmlRegisterType<ModelFactory>("com.example.qml", 1, 0, "ModelFactory");

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    return app.exec();
}

你看到了什么:

runtime

点击任何名称。它会工作一次,之后它们将被取消定义,因为model变为空。

例如

qml: clicked on Sally
~BoxModel()
qml: clicked on undefined

我的问题是为什么是这个,当我还有它的参考时?

在示例中,onClicked可以更改为label.text而不是model.name来修复,但真正的问题通常是{任何时候,对象都可以访问{1}}任何数据。例如,当盒子需要重绘时。随机数据消失,取决于GC。

我尝试过让c ++管理动态模型的生命。如果我知道 确切地说QML完成了它,这可能会有用。

感谢您提供信息和想法。

在Windows 8.1 / qt5.6mingw上运行

EDIT1:文件作为要点, https://gist.github.com/anonymous/86118b67ec804e6149423c14792f312d

3 个答案:

答案 0 :(得分:0)

正如库巴所说,这确实看起来像一个错误。但是,您可以采用另一种方法,并通过QQmlEngine::setObjectOwnership()自行获取模型的所有权。具体来说,改变

Q_INVOKABLE BoxModel* makeModel()
{
    return new BoxModel();
}

Q_INVOKABLE BoxModel* makeModel()
{
    BoxModel *model = new BoxModel(this);
    QQmlEngine::setObjectOwnership(model, QQmlEngine::CppOwnership);
    return model;
}

将修复此问题(请记住将返回的模型父级设为BoxModel,以便适当删除)。行为的原因解释为here

  

通常,应用程序不需要显式设置对象的所有权。 QML使用启发式方法设置默认所有权。默认情况下,QML创建的对象具有JavaScriptOwnership。例外情况是通过调用QQmlComponent :: create()或QQmlComponent :: beginCreate()创建的根对象,默认情况下具有CppOwnership。这些根级对象的所有权被认为已传输到C ++调用者。

     

QML未创建的对象默认具有CppOwnership。对此的例外是从C ++方法调用返回的对象;他们的所有权将设置为JavaScriptOwnership。这仅适用于Q_INVOKABLE方法或槽的显式调用,但不适用于属性getter调用。

答案 1 :(得分:0)

我遇到了与ComboBox相同的问题。

作为一种解决方法,您可以创建自己的属性以对其进行强有力的引用:

Repeater {
    property QtObject myModel: maker.makeModel();
    model: myModel
    // …
}

答案 2 :(得分:0)

我知道这是一个老问题,但我刚遇到类似问题,并在撰写我的问题时发现了你的问题。有关完整故事,请参阅QObject gets destroyed after being put into QML variable,我将在此处引用它。

  

我已经知道如果我在将QObject传递给QML之前设置它的父节点,那么它就不会被删除。因此,我得出的结论是,将未经授权的QObject传递到QML范围会使该范围成为QObject的父级,并在范围结束后调用其析构函数。