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
AD

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

  • Результат:50бали,
  • Рейтинг балів-4
m
  • molni99
  • 26 жовтня 2024 р. 01:37

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

  • Результат:80бали,
  • Рейтинг балів4
m
  • molni99
  • 26 жовтня 2024 р. 01:29

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

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 11:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 жовтня 2024 р. 14:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 08:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 07:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 11:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi24 червня 2024 р. 15:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 06:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 03:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 09:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

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