QML QQmlPropertyList — содержит время жизни объекта и «правила памяти».

Мне трудно подтвердить правила владения объектами элементов, вставленных в QQmlPropertyList, когда он настроен как часть компонента QML, определенного на C++ с помощью

Q_CLASSINFO("DefaultProperty", "values")

DTech задал нечто подобное в: QQmlListProperty - QList с возможностью записи нарушает правила управления памятью QML?, но его беспокоили последствия удаления компонентов, определенных в QML. Меня больше беспокоят требования жизненного цикла объектов, вставленных в список в целом.

Теперь из того, что я понимаю:

  1. Если дочерние элементы созданы и вставлены из QML, их родители будут установлены автоматически, а объекты будут управляться QmlEngine. (родитель должен быть уже определен при добавлении)

  2. Если я вставляю что-то со стороны C++, мне нужно самому управлять временем жизни. (родительский элемент будет nullptr при добавлении)

Документы QT предупреждают о «нарушении правил управления памятью QML». в http://doc.qt.io/qt-5/qqmllistproperty.html но я не могу найти ничего, что действительно говорит об этом в ясной и лаконичной форме.

В настоящее время я реализовал оболочку вокруг вектора, очень похожего на http://doc.qt.io/qt-5/qtqml-referenceexamples-properties-example.html

Однако я добавил два правила:

  1. Добавление проверяет, имеет ли добавляемый объект родителя nullptr и, если да, становится владельцем.
  2. При очистке, если какой-либо объект принадлежит текущему родительскому элементу списка, выполняется 'deleteLater' для объекта и устанавливается его родитель в nullptr

Я предполагаю, что, поскольку мы устанавливаем родителя при добавлении объектов с родителями nullptr, QT автоматически удалит принадлежащие QObjects, когда родитель выйдет за пределы области видимости.

Есть ли еще правила, которые мне не хватает и о которых нужно знать?

Код для моей реализации на тот случай, если приведенное выше требует небольшого пояснения:

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);
  }

};


person Andrew Goedhart    schedule 07.06.2018    source источник


Ответы (1)


С точки зрения того, кто открыл для себя сомнительное управление временем существования объекта в QML и боролся с этим почти 3 года, за это время я не сталкивался ни с одним случаем неправильного обращения с декларативно определенными объектными деревьями.

Проблемы связаны с объектами, которые создаются динамически, они, как правило, время от времени удаляются независимо от того, есть ли у них родитель или сколько активных ссылок на них существует в коде. Если вы хотите гарантировать, что они не будут удалены, предоставьте им явное право собственности на CPP.

Это предотвратит уничтожение движком этих объектов, пока они все еще используются. Объекты по-прежнему будут собираться после уничтожения их родителей, как это происходит в C++ API.

Обратите внимание, что это предупреждение присутствует только в конструкторе, который принимает QList, а не в том, который принимает указатели управляющих функций. Так что, во всяком случае, вы можете, по крайней мере, найти утешение в том, что вы не используете этот формат. Я провел множество тестов на обоих и не обнаружил никаких функциональных различий. Это предупреждение могло быть неуместным, плохо сформулированным и даже совершенно бессмысленным в дополнение к тому, что оно не содержало никакого контекста того, что оно означает и каковы могут быть его последствия. Просто будьте тщательны при тестировании, чтобы выявить любые проблемы в зачаточном состоянии.

person dtech    schedule 07.06.2018