endRemoveRows() and QML ListView deleteLater() synchronization
-
Hello,
I've stumbled on curious problem. If IdeleteLater()
object after calingendRemoveRows()
. ListView delegates will not get destroyed in time and every binding to the deleted item will results in a lot ofTypeError: Cannot read property 'foo' of null
Here's an example:
Let's preted that I have defined C++ Itemclass MyItem : public QObject { Q_OBJECT Q_PROPERTY(QString foo READ foo WRITE setFoo NOTIFY fooChanged) ... }
And I put these items into a
QAbstractListModel
class MyModel : public QAbstractListModel { Q_OBJECT public: QHash<int, QByteArray> Model::roleNames() const { QHash<int, QByteArray> roles; roles[Qt::UserRole] = "object"; return roles; } ... }
where
data()
withObjectRole
returns*Item
and I remove these Items like thisMyModel::removeItem(MyÏtem* item) { if (item && m_items.contains(item)) { int index = m_items.indexOf(item); beginRemoveRows(QModelIndex(), index, index); m_items.removeAt(index); endRemoveRows(); item->deleteLater(); }
Finally my QML for this model can be simple as this
ListView { delegate: Text { property MyItem myItem: model.object text: myItem.foo } }
After removing item from model, Qt will fire that TypeError. but item will be destroyed both from C++ and from QML. But if I stall deleteLater like this
QTimer::singleShot(10, [=]{ item->deleteLater();});
then everything will works okay. Also I have to use this nesting because my Items are quite big and I connect their signals and pass their pointer to a lot of places. I also have to delete object, because the item holds a lot of memory that I need to be freed.
It's obvious the C++ deletes object much faster than QML destroys ListView delegate. The question is, how can my C++ Model know when it is safe to deleteLater()? -
Had the same problem.
Fixed it by not binding the object referenced by the role to a property and using the property but using the role name directly to acces the model item.
Instead of
property MyItem myItem: model.object text: myItem.foo
try
text: model.object.foo