QPropertyBinding: Do not reevaluate if not installed on property

Since we changed binding evaluation to be always eager, we notify and
evaluate all bindings as soon as any dependency changes. This includes
bindings which have been initially installed on a property, but which
were later removed.
With lazy evaluation, we would only notify those bindings and mark them
as dirty, which is unproblematic. With eager evalution, we attempt to
evaluate the binding, though. While that part is still fine, afterwards
we would attempt to write the new value into the property. However,
there is no property at that point, as the binding is not installed.
Instead of adding a check whether the propertydataptr is null, we skip
the reevaluation completely by removing the bindings observers - and
thus the cause for the binding function's reevaluation. As soon as the
binding is set, we reevaluate the function anyway, at which point we
also capture the observers again.

Task-number: QTBUG-89505
Change-Id: Ie1885ccd8be519fb96f6fde658275810b54f445a
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
bb10
Fabian Kosmale 2021-05-10 15:50:12 +02:00
parent e381977b21
commit 5b681bea90
2 changed files with 28 additions and 0 deletions

View File

@ -273,6 +273,7 @@ QPropertyBindingPrivate::~QPropertyBindingPrivate()
void QPropertyBindingPrivate::unlinkAndDeref()
{
clearDependencyObservers();
propertyDataPtr = nullptr;
if (--ref == 0)
destroyAndFreeMemory(this);

View File

@ -107,6 +107,7 @@ private slots:
void noDoubleNotification();
void groupedNotifications();
void groupedNotificationConsistency();
void uninstalledBindingDoesNotEvaluate();
};
void tst_QProperty::functorBinding()
@ -1747,6 +1748,32 @@ void tst_QProperty::groupedNotificationConsistency()
QVERIFY(areEqual); // value changed runs after everything has been evaluated
}
void tst_QProperty::uninstalledBindingDoesNotEvaluate()
{
QProperty<int> i;
QProperty<int> j;
int bindingEvaluationCounter = 0;
i.setBinding([&](){
bindingEvaluationCounter++;
return j.value();
});
QCOMPARE(bindingEvaluationCounter, 1);
// Sanity check: if we force a binding reevaluation,
j = 42;
// the binding function will be called again.
QCOMPARE(bindingEvaluationCounter, 2);
// If we keep referencing the binding
auto keptBinding = i.binding();
// but have it not installed on a property
i = 10;
QVERIFY(!i.hasBinding());
QVERIFY(!keptBinding.isNull());
// then changing a dependency
j = 12;
// does not lead to the binding being reevaluated.
QCOMPARE(bindingEvaluationCounter, 2);
}
QTEST_MAIN(tst_QProperty);
#undef QT_SOURCE_LOCATION_NAMESPACE