Is QQmlListProperty(QObject * object, QList<T *> & list) safe to use?
-
The docstring for "this constructor":http://qt-project.org/doc/qt-5.0/qtqml/qqmllistproperty.html#QQmlListProperty claims:
bq. Generally this constructor should not be used in production code, as a writable QList violates QML's memory management rules. However, this constructor can very useful while prototyping.
However, "Exposing Attributes of C++ Types to QML":http://qt-project.org/doc/qt-5.0/qtqml/qtqml-cppintegration-exposecppattributes.html#properties-with-object-list-types seems to recommend its use:
bq. QQmlListProperty is a template class that can be conveniently constructed from a QList value.
...
The MessageBoard::messages() function simply creates and returns a QQmlListProperty from its QList<T> m_messages member, passing the appropriate list modification functions as required by the QQmlListProperty constructor:Is the constructor docstring out-of-date? Or is the tutorial misleading? How is a writable QList in violation of QML's memory management rules?
thanks,
-Mark -
Hi,
This is actually a really interesting question, and one I didn't know the answer to - I had to ask Andrew den Exter who explained it to me. Basically, it means that because (with this ctor) the type doesn't have a chance to define the append / clear / whatever functions, anyone who modifies the content of the list does so "without informing the owner of the list". This means that if the content of list is owned by the owner of the list property, that content will be leaked if it is cleared. Similarly, if content is appended to the list and it has no owner (and thus the list property owner "should" take ownership via QObject parenting), that will not happen either.
In short: "it allows the content of the QList to be modified without any notification to the owner of that list".
Aside from that issue, QDeclarativeListProperty is a tricky beast which (imo) needs careful consideration when writing new code, even when the "safe" API is used. For example:
@
// imagine TWDLP is a "type with declarative list property" which has a DLP called "filters".
// the "filters" property takes a list of Filter instances, say.
// and imagine that TWDLP offers a Q_INVOKABLE QString matches(const QVariantMap &trie) const;
// which uses the filters property to return a concat string of things which match the filters.
TWDLP {
id: rootproperty var trie: { "a": ["abcde", "abdef"], "d": ["defab", "deabf"] } Filter { id: filterOne criteria: "abc" } Filter { id: filterTwo criteria: "def" } Text { text: root.matches(trie) } Button { id: toggleFilter property int whichFilter: 2 onClicked: { if (whichFilter == 1) { root.filters = [ filterTwo ] whichFilter = 2 } else { root.filters = [ filterOne ] whichFilter = 1 } } }
}
@In this instance, the assignment is an interesting one, since you definitely do not want to delete the existing contents of the list if it had a QObject parent when it was originally assigned - even if that parent is the owner of the list property (as in this case).
I hit this issue a while ago in an API I was writing, which is why it stuck in my memory. See https://github.com/nemomobile/nemo-qml-plugins/commit/fe175e122294b0c0f98396b239353b91d0bb4bd9 for that one ;-)
Cheers,
Chris.