Betty Crokker
Betty Crokker

Reputation: 3351

Do Qt properties assume objects are pointers and not members?

Let's say I have a C++ object with a member object that I expose to QML:

class X : public QObject
{
   Q_OBJECT
};
class Y : public QObject
{
   Q_OBJECT
   Q_PROPERTY(X* x READ getX CONSTANT)
public:
   X* getX(void) { return &x; }
   X x;
};

Most of the time this works, but sometimes it will cause crashes. The call stack is quite lengthy but goes like this:

QScopedPointer<QObjectData, QScopedPointerDeleter<QObjectData> >::data
qGetPtrHelper<QScopedPointer<QObjectData>>
QObject::d_func
QObjectPrivate::get
QQmlNotifierEndpoint::disconnect
QQmlNotifierEndpoint::~QQmlNotifierEndpoint
QQmlJavaScriptExpressionGuard::~QQmlJavaScriptExpressionGuard
QRecyclePool<QQmlJavaScriptExpressionGuard, 1024>::Delete
QQmlJavaScriptExpressionGuard::Delete
QQmlJavaScriptExpression::GuardCapture::captureProperty
QQmlEnginePrivate::captureProperty
QV4::QObjectWrapper::getProperty
etc.

If instead I set X to be a pointer:

class Y : public QObject
{
   Q_OBJECT
   Q_PROPERTY(X* x READ getX CONSTANT)
public:
   Y()
   { x = new X; }
   X* getX(void) { return x; }
   X* x;
};

The crashes go away.

Is this a known restriction of Q_PROPERTY, that if you return a pointer to a QObject, that object is assumed to be a pointer (not a member) and things like deleteLater() might be called?

Upvotes: 2

Views: 1208

Answers (1)

GrecKo
GrecKo

Reputation: 7150

This crash most likely happens because QML takes ownership of the object c your function returns, and want to delete it at some point late. In your first example, since x is not allocated from the free store, trying to delete it will crash. In the second example, the QML engine still tries to delete it, but it does so without problem.

The question to ask is why does the QML engine takes ownership of the object ? The documentation states in Data Type Conversion Between QML and C++ | Data Ownership :

When data is transferred from C++ to QML, the ownership of the data always remains with C++. The exception to this rule is when a QObject is returned from an explicit C++ method call: in this case, the QML engine assumes ownership of the object, unless the ownership of the object has explicitly been set to remain with C++ by invoking QQmlEngine::setObjectOwnership() with QQmlEngine::CppOwnership specified.

Additionally, the QML engine respects the normal QObject parent ownership semantics of Qt C++ objects, and will not ever take ownership of a QObject instance which already has a parent.

So, in order to remedy to your problem, you could either affect a QObject parent to your x object, or explicitely declaring it having QmlEngine::CppOwnership.

Upvotes: 5

Related Questions