QML中使用的单例导致退出时崩溃

时间:2016-02-01 22:12:55

标签: c++ qt singleton qml

我没有严格遵循Qt中的doc。而不是使用新的我使用静态局部变量来能够调用单例类的析构函数。差异在here中描述。

我的问题是退出我的程序时,我的一个单例类导致它崩溃。在调试模式下,调试器将停止在该类的析构函数中。这个单例类和其他类之间的不同之处在于我的QML和cpp文件都使用了这个。

我的问题是我是否必须使用new关键字为此单例分配实例?

class AppConfiguration : public QObject
{
    Q_OBJECT
    Q_DISABLE_COPY(AppConfiguration)

    Q_PROPERTY(bool serverMode READ serverMode WRITE setServerMode NOTIFY serverModeChanged)

public:
    static QObject* instance(QQmlEngine* engine = NULL, QJSEngine* scriptEngine = NULL);
    ~AppConfiguration();

protected:
    AppConfiguration();

private:
    static QObject* s_instance;
};

CPP

QObject* AppConfiguration::s_instance = NULL;

QObject* AppConfiguration::instance(QQmlEngine *engine,
                                        QJSEngine *scriptEngine)
{
    Q_UNUSED(engine)
    Q_UNUSED(scriptEngine)

    static AppConfiguration s_instance;

    return &s_instance;
}

AppConfiguration::AppConfiguration() :
    m_serverMode(true)
{

}

AppConfiguration::~AppConfiguration()
{

}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    qmlRegisterSingletonType<AppConfiguration>("com.synergy.gui", 1, 0, "AppConfiguration", &AppConfiguration::instance);

    QQmlApplicationEngine engine(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

QML

Switch {
    id: modeSwitch
    x: 4
    y: 31
    transformOrigin: Item.Center
    checked: AppConfiguration.serverMode
    Binding {
        target: AppConfiguration
        property: "serverMode"
        value: modeSwitch.checked
    }
    Connections {
        target: AppConfiguration
            onServerModeChanged: {
                modeSwitch.checked = AppConfiguration.serverMode
            }
    }
}

2 个答案:

答案 0 :(得分:6)

你自己回答了这个问题:

  

我没有严格遵循Qt中的文档。而不是使用新的我使用静态局部变量来调用单例类的析构函数。

这是不允许的。文档告诉你:

  

注意:从单例类型返回的QObject单例类型实例   提供商归QML引擎所有。出于这个原因,单身人士   类型提供程序函数不应该实现为单例   工厂。

Qml以每种QQmlEngine的方式工作,AppConfiguration::instance函数将被调用。 QML-singeltons是每个引擎的单位数,而不是每个应用程序。因此,一旦引擎被破坏,Qt将自动销毁实例。

你得到一个错误,因为这样同一个对象被破坏两次,一旦QML引擎被破坏,最后,在关闭时,静态段被清除。多数民众赞成在崩溃。

要解决此问题,您需要为QQmlEngine设置一个实例,即使用new创建实例。没有其他办法。要获得全局singelton,请尝试创建2个类:

  1. 真正的singelton类,现在使用rigth
  2. 一个“包装器”类,它将单例属性/信号/槽转发给QML,但它本身不是一个单一的。
  3. 修改

    要通过2个类实现它,您可以创建以下代码:
    第一类(真正的单身人士)基本保持不变:

    appconfiguration.h

    class AppConfiguration : public QObject
    {
        Q_OBJECT
        Q_DISABLE_COPY(AppConfiguration)
    
        Q_PROPERTY(bool serverMode READ serverMode WRITE setServerMode NOTIFY serverModeChanged)
    
    public:
        static AppConfiguration* instance();
        ~AppConfiguration();
    
    protected:
        AppConfiguration();
    
    private:
        static AppConfiguration s_instance;//is static -> destructor will be called
    };
    

    appconfiguration.cpp

    AppConfiguration AppConfiguration::s_instance;
    
    AppConfiguration* AppConfiguration::instance()
    {
        return &AppConfiguration::s_instance;//just return the static instance here
    }
    
    //...
    

    现在是第二节课。由于它需要具有QML属性,因此您必须创建新的标头和源。例如,您可以将其命名为QmlAppConfiguration

    qmlappconfiguration.h

    class QmlAppConfiguration : public QObject
    {
        Q_OBJECT
        Q_DISABLE_COPY(QmlAppConfiguration)
    
        //forward all properties/signals/slots...
        Q_PROPERTY(bool serverMode READ serverMode WRITE setServerMode NOTIFY serverModeChanged)
    
    public:
        static QObject* instance(QQmlEngine* engine = NULL, QJSEngine* scriptEngine = NULL);
    
        bool serverMode() const;
    
    public slots:
        void setServerMode(bool serverMode);
    
    signals:
        void serverModeChanged(bool serverMode);
    
    private:
        QmlAppConfiguration();
    }
    

    qmlappconfiguration.cpp

    QObject* QmlAppConfiguration::instance(QQmlEngine *engine, QJSEngine *scriptEngine)
    {
        Q_UNUSED(engine)
        Q_UNUSED(scriptEngine)
    
        return new QmlAppConfiguration();// a new instance for each engine
    }
    
    bool QmlAppConfiguration::serverMode() const
    {
        return AppConfiguration::instance()->serverMode();//forwarded
    }
    
    void QmlAppConfiguration::setServerMode(bool serverMode) const
    {
        AppConfiguration::instance()->setServerMode(serverMode);//forwarded
    }
    
    QmlAppConfiguration::QmlAppConfiguration()
    {
        //to make shure signals get forwared, connect them here
        //and yes, you can connect signals to signals
        connect(AppConfiguration::instance(), &AppConfiguration::serverModeChanged,
                this, &QmlAppConfiguration::serverModeChanged);
    }
    

    最后但并非最不重要的是,修改您的主要内容。如果您按照下面所述的方式进行操作,则无需修改QML文件:

    int main(int argc, char **argv)
    {
        //...
        qmlRegisterSingletonType<QmlAppConfiguration>("com.synergy.gui", 1, 0, "AppConfiguration", &QmlAppConfiguration::instance);
        //...
    }
    

    就是这样!现在你有1个真正的单例类,AppConfiguration,可以在c ++中使用。由于它是一个静态实例,因此将在退出时调用析构函数。在QML中,您现在拥有一个名为AppConfiguration的类,但在内部使用QmlAppConfiguration的(多个)实例,这些实例只是真正的单例类的“委托”。我希望这会有所帮助。

答案 1 :(得分:5)

AppConfiguration :: instance 功能中,您只需添加一行:

QQmlEngine::setObjectOwnership(&s_instance, QQmlEngine::CppOwnership);

所以全功能定义如下:

QObject* AppConfiguration::instance(QQmlEngine *engine,
                                    QJSEngine *scriptEngine) {
    Q_UNUSED(engine)
    Q_UNUSED(scriptEngine)

    static AppConfiguration s_instance;

    QQmlEngine::setObjectOwnership(&s_instance, QQmlEngine::CppOwnership);
    return &s_instance;
}