Reputation: 1812
I didn't strictly follow the doc in Qt. Instead of using new I use static local variable to be able to invoke the destructor of the singleton class. The difference is described in here.
My problem is when exit my program, one of my singleton class causes it crash. In debug mode, the debugger would stop at the destructor of that class. The different between this singleton class and the others is this one is used in both of my QML and cpp files.
My question is do I have to use new keyword to allocate an instance for this singleton?
header
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()
{
}
main
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
}
}
}
Upvotes: 2
Views: 2384
Reputation: 101
In AppConfiguration::instance function you can just add a line:
QQmlEngine::setObjectOwnership(&s_instance, QQmlEngine::CppOwnership);
So full function definition will be look like:
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;
}
Upvotes: 7
Reputation: 7146
You answered the question yourself:
I didn't strictly follow the doc in Qt. Instead of using new I use static local variable to be able to invoke the destructor of the singleton class.
This is not allowed. The documentation tells you:
NOTE: A QObject singleton type instance returned from a singleton type provider is owned by the QML engine. For this reason, the singleton type provider function should not be implemented as a singleton factory.
Qml works in a ways that for every QQmlEngine
, the AppConfiguration::instance
function will be called. QML-singeltons are singeltons per engine, not per application. Thus, Qt will automatically destroy the instance once the engine gets destroyed.
You get an error, because this way the same object gets destroyed twice, once the QML-engine gets destroyed and finally, on shutdown, when the static segment is getting cleaned up. Thats were it crashes.
To solve this problem, you need to have one instance per QQmlEngine
, i.e. create them with new
. There is no other way. To still get your global singelton, try to create 2 classes:
EDIT:
To realize it with 2 classes, you could create the following code:
The first class (the real singleton) stays mostly unchanged:
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
}
//...
Now the second class. Since it needs to have properties for QML, you will have to create a new header and source. You could, for example, name it 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);
}
Last but not least, modify your main. If you do it the way stated below, you don't have to modify your QML files:
int main(int argc, char **argv)
{
//...
qmlRegisterSingletonType<QmlAppConfiguration>("com.synergy.gui", 1, 0, "AppConfiguration", &QmlAppConfiguration::instance);
//...
}
And thats it! Now you have 1 true singleton class, the AppConfiguration
, that can be used in c++. Since it's a static instance, the destructor will be called on exit. In QML, you now have a class that is name AppConfiguration
too, but internally uses (multiple) instances of QmlAppConfiguration
, that all are just "delegates" to the real singleton class. I hope this could help.
Upvotes: 7