I am finding it difficult to confirm the rules for object ownership of items inserted into QQmlPropertyList when it is setup as part of a QML component defined in C++ with
Q_CLASSINFO("DefaultProperty", "values")
DTech asked something similar in : QQmlListProperty - writable QList violates QML's memory management rules?, but he was worried about the consequences of deleting components defined in QML. I am more worried about the life cycle requirements of the objects inserted into the list in general.
Now from what I understand:
If the children are created and inserted from QML their parents will be automatically set and the objects are managed by the QmlEngine. (parent should already be defined on append)
If I insert something from the C++ side I need to manage the lifetime myself. (parent will be nullptr on append)
The QT docs for warn against 'violating QML's memory management rules.' in http://doc.qt.io/qt-5/qqmllistproperty.html but I can't find anything that actually states these in a clear, concise way.
Currently I have implemented a wrapper around a vector very similar to http://doc.qt.io/qt-5/qtqml-referenceexamples-properties-example.html
However I added two rules:
- Appending checks to see if the object being added has a nullptr parent and if so takes ownership.
- On clear if any object is owned by the current list parent, executes 'deleteLater' on the object and sets its parent to nullptr
I am assuming that because we set the parent when adding objects with nullptr parents, QT will delete the QObjects owned automatically when the parent goes out of scope.
Are there anymore more rules that I am missing and need to be aware of ?
Code for my implementation just in case the above requires a bit of clarification :
template <typename T>
class QmlListFacade {
private:
std::vector<T *> _storage;
static void append(QQmlListProperty<T> *list, T *newValue) {
// take ownership of the object if none are currently defined
auto obj = static_cast<QObject *>(newValue);
if( obj->parent() == nullptr) {
obj->setParent(list->object);
}
auto internalList = reinterpret_cast<QmlListFacade *>(list->data);
internalList->_storage.push_back(newValue);
}
static int count(QQmlListProperty<T> *list) {
return reinterpret_cast<QmlListFacade *>(list->data)->_storage.size();
}
static T *get(QQmlListProperty<T> *list, int index) {
return reinterpret_cast<QmlListFacade *>(list->data)->_storage[index];
}
static void clear(QQmlListProperty<T> *list) {
auto internalList = reinterpret_cast<QmlListFacade *>(list->data);
for( auto item :internalList->_storage){
// only delete if we are the owners.
auto obj = static_cast<QObject *>(item);
if( obj->parent() == list->object){
obj->setParent(nullptr);
obj->deleteLater();
}
}
return internalList->_storage.clear();
}
public:
QmlListFacade() = default;
QQmlListProperty<T> values(QObject *parent) {
return QQmlListProperty<T>(parent, this, &QmlListFacade::append, &QmlListFacade::count, &QmlListFacade::get,
&QmlListFacade::clear);
}
};