diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index f3ea22d25db..fafcd38417e 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -943,6 +943,11 @@ QObject::QObject(QObjectPrivate &dd, QObject *parent) Q_TRACE(QObject_ctor, this); } +void QObjectPrivate::clearBindingStorage() +{ + bindingStorage.clear(); +} + /*! Destroys the object, deleting all its child objects. @@ -973,6 +978,10 @@ QObject::~QObject() d->wasDeleted = true; d->blockSig = 0; // unblock signals so we always emit destroyed() + // If we reached this point, we need to clear the binding data + // as the corresponding properties are no longer useful + d->clearBindingStorage(); + QtSharedPointer::ExternalRefCountData *sharedRefcount = d->sharedRefcount.loadRelaxed(); if (sharedRefcount) { if (sharedRefcount->strongref.loadRelaxed() > 0) { diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index 4f38796c81c..6d5044e99c8 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -326,6 +326,8 @@ public: QObjectPrivate(int version = QObjectPrivateVersion); virtual ~QObjectPrivate(); void deleteChildren(); + // used to clear binding storage early in ~QObject + void clearBindingStorage(); inline void checkForIncompatibleLibraryVersion(int version) const; diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp index 43fc84159a0..0df73ad6393 100644 --- a/src/corelib/kernel/qproperty.cpp +++ b/src/corelib/kernel/qproperty.cpp @@ -2138,6 +2138,12 @@ QBindingStorage::~QBindingStorage() QBindingStoragePrivate(d).destroy(); } +void QBindingStorage::clear() +{ + QBindingStoragePrivate(d).destroy(); + d = nullptr; +} + // ### Unused, retained for BC with 6.0 void QBindingStorage::maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const { diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index 5deb8fa9b79..9c002f378ff 100644 --- a/src/corelib/kernel/qproperty.h +++ b/src/corelib/kernel/qproperty.h @@ -945,6 +945,7 @@ class Q_CORE_EXPORT QBindingStorage template friend class QObjectCompatProperty; + friend class QObjectPrivate; public: QBindingStorage(); ~QBindingStorage(); @@ -973,6 +974,7 @@ public: return bindingData_helper(data, create); } private: + void clear(); void registerDependency_helper(const QUntypedPropertyData *data) const; // ### Unused, but keep for BC void maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const; diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp index d1dc0ceacf9..735d45da2a5 100644 --- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp +++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp @@ -57,6 +57,7 @@ private slots: void basicDependencies(); void multipleDependencies(); void bindingWithDeletedDependency(); + void dependencyChangeDuringDestruction(); void recursiveDependency(); void bindingAfterUse(); void bindingFunctionDtorCalled(); @@ -199,6 +200,35 @@ void tst_QProperty::bindingWithDeletedDependency() QCOMPARE(propertySelector.value(), staticProperty.value()); } +class ChangeDuringDtorTester : public QObject +{ + Q_OBJECT + Q_PROPERTY(int prop READ prop WRITE setProp BINDABLE bindableProp) + +public: + void setProp(int i) { m_prop = i;} + int prop() const { return m_prop; } + QBindable bindableProp() { return &m_prop; } +private: + Q_OBJECT_COMPAT_PROPERTY(ChangeDuringDtorTester, int, m_prop, &ChangeDuringDtorTester::setProp) +}; + +void tst_QProperty::dependencyChangeDuringDestruction() +{ + auto tester = std::make_unique(); + QProperty iprop {42}; + tester->bindableProp().setBinding(Qt::makePropertyBinding(iprop)); + QObject::connect(tester.get(), &QObject::destroyed, [&](){ + iprop = 12; + }); + bool failed = false; + auto handler = tester->bindableProp().onValueChanged([&](){ + failed = true; + }); + tester.reset(); + QVERIFY(!failed); +} + void tst_QProperty::recursiveDependency() { QProperty first(1);