Evgenii Legotckoi
Evgenii Legotckoi02 лютого 2016 р. 11:10

QML - Урок 023. Полювання на помилки в покажчику передачі на QObject в QML

Одними з наймерзкіших і мало передбачуваних багів є ті, що виникають у невизначений момент часу. До таких можна віднести баг, який проявляється при передачі покажчика на QObject в QML шар. Проблема полягає в тому, що якщо у QObject відсутній батько, то при передачі до шару QML відбувається зміна власника об'єкта, тобто йому встановлюється JavaScriptOwnership. У результаті, коли у QML шарі пропадуть усі посилання на даний об'єкт, то він буде видалений збирачем сміття QML. Відповідно, всі посилання в C++ шарі виявляться невалідними. А програма при спробі звернення за цими посиланнями мовчки схлопнеться, нічого не повідомивши про причину краху.

Говорячи про невизначеність спрацьовування бага, слід зазначити, що ця невизначеність виростає з моменту складання сміття. У звичайному варіанті цей момент може наступити тоді, коли програма відкушає пару гігабайт пам'яті або коли пам'яті не вистачатиме і її можна буде отримати, зробивши складання сміття. Тобто програма може без проблем працювати досить довго без прояву бага, і навіть можливо пережити кілька версій, перш ніж баг виявиться у якогось користувача, який вирішить повідомити про це розробникам.

Варто зазначити, що це правило не застосовуватиметься до об'єктів оголошених як Q_PROPERTY.

Але для демонстрації проблеми можна скористатися QML функцією gc() , яка прискорить збирання сміття.


Полювання на помилок

А тепер подивимося, як це реалізовано у програмному коді.

Структура проекта

У цьому проекті нас цікавлять такі файли:

  • main.cpp - без його редагування не вдасться передати випробуваний об'єкт у QML шар;
  • MyClass.h - заголовний файл класу для виробництва проблем;
  • MyClass.cpp - файл вихідних кодів класу для проблем;
  • main.qml - файл QML шару, в якому знищуватимемо покажчик на об'єкт.

MyClass.h

У тестованому класі буде створюватися об'єкт за допомогою методу createObject (), а використовуватися за допомогою методу useObject (), відповідно. Як тільки в шарі QML буде обнулено покажчик і буде зроблено складання сміття, то метод useObject() покладе програму.

#ifndef MYCLASS_H
#define MYCLASS_H

#include <QObject>

class MyClass : public QObject
{
    Q_OBJECT

public:
    Q_INVOKABLE QObject* createObject();    // Создание объекта для испытаний
    Q_INVOKABLE void useObject();           // Использование испытуемого объекта

private:
    QObject* m_object;      // Подопытный кролик
};

#endif // MYCLASS_H

MyClass.cpp

У методу createObject() є два варіант ініціалізації об'єкта m_object. Перший не закоментований є варіантом з багом, другий буде працювати стабільно. А в методі useObject() отримуватимемо ім'я об'єкта, природно, що в даному випадку будемо отримувати порожній рядок.

#include "MyClass.h"
#include <QObject>
#include <QDebug>
#include <QString>

QObject *MyClass::createObject()
{
    m_object = new QObject;         // Создаём объект без родителя
//    m_object = new QObject(this);   // Создаём объект с родителем
    return m_object;                // Возвращаем объект
}

void MyClass::useObject()
{
    qDebug() << m_object->objectName(); // Отображаем имя объекта
}

main.cpp

Стандартна реєстрація об'єкта для доступу до контексту QML.

#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "MyClass.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    MyClass mClass;

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    // Регистрируем тестовый класс в QML слое, демонстрирующий проблему
    engine.rootContext()->setContextProperty("myClass", &mClass);

    return app.exec();
}

main.qml

У проект створено три кнопки:

  1. Створить об'єкт та передасть його покажчик властивості objectFromCppWorld;
  2. Обнулити покажчик, створивши баг;
  3. Намагається використовувати об'єкт C++ шару. І метод буде працювати, доки не відбудеться складання сміття.
import QtQuick 2.5
import QtQuick.Controls 1.3

ApplicationWindow {
    width: 640
    height: 480
    visible: true

    property QtObject objectFromCppWorld: null

    /* Для демонстрации проблемы со сменой родителя объекта
     * сделаем три кнопки, которые будем активировать последовательно.
     * Первая создаст объект в C++ слое и передаст указатель на него в QML слой
     * Вторая обнулит указатель и запустит сборку мусора
     * Третья попробует использовать объект
     */
    Column {
        anchors.centerIn: parent
        Button {
            text: "create object in C++, save the pointer to it in C++ world and pass it to QML"
            onClicked: objectFromCppWorld = myClass.createObject()
        }
        Button {
            text: "null the reference in QML"
            onClicked: {
                objectFromCppWorld = null
                gc()
            }
        }
        Button {
            // Когда произойдет сборка мусора, использовать объект без родителя не получится
            text: "use created object in C++"
            onClicked: myClass.useObject()
        }
    }
}

Соавтор статті: Володимир Курман

Відеоурок

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
Дмитрий

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:60бали,
  • Рейтинг балів-1
Дмитрий

C++ - Тест 003. Условия и циклы

  • Результат:92бали,
  • Рейтинг балів8
d
  • dsfs
  • 26 квітня 2024 р. 11:56

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80бали,
  • Рейтинг балів4
Останні коментарі
k
kmssr09 лютого 2024 р. 02:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко05 лютого 2024 р. 09:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 грудня 2023 р. 18:30
Boost - статичне зв&#39;язування в проекті CMake під Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 грудня 2023 р. 16:38
Boost - статичне зв&#39;язування в проекті CMake під Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik19 грудня 2023 р. 05:01
Qt/C++ - Урок 056. Підключення бібліотеки Boost в Qt для компіляторів MinGW і MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Тепер обговоріть на форумі
G
George1307 травня 2024 р. 07:27
добавить qlineseries в функции в функции: "GPlotter::addSeries(QString title, QVector &arr)" я вызываю метод setChart(...), я в конструктор передал адрес на QChartView элемент
BlinCT
BlinCT05 травня 2024 р. 12:46
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
PS
Peter Son04 травня 2024 р. 00:57
Best Indian Food Restaurant In Cincinnati OH Ready to embark on a gastronomic journey like no other? Join us at App india restaurant and discover why we're renowned as the Best Indian Food Restaurant In Cincinnati OH . Whether y…
Evgenii Legotckoi
Evgenii Legotckoi02 травня 2024 р. 21:07
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.
IscanderChe
IscanderChe30 квітня 2024 р. 11:22
Во Flask рендер шаблона не передаётся в браузер Доброе утро! Имеется вот такой шаблон: <!doctype html><html> <head> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_…

Слідкуйте за нами в соціальних мережах