Cannot destroy C++ Object in QML
-
wrote on 1 Aug 2016, 18:43 last edited by
Hi,
in my code I want to delete an object that I pass to QtQuick in order to prevent a memory leak. However, I fail to be able to call 'deleteLater' from QML. It does not get exposed as a slot and when I create my own slot that destroys the object I get the error message "Invalid attempt to destroy() an indestructible object". Is there a way I can fix this?
-
wrote on 1 Aug 2016, 21:22 last edited by
How was the object created?
It may be easier to use QQmlEngine::setObjectOwnership() to assign the object to the QML engine, and allow garbage collection to handle it. -
wrote on 2 Aug 2016, 12:01 last edited by
Thanks for your reply!
The object is created on the heap in C++. I'd prefer a solution that would not require to have it handled by JavaScript.
My use-case is this: The object wraps a task. If it fails the user is prompted (by a QML MessageDialog) whether there should be a retry (using the same object) or whether it should be canceled. In case of canceling I need to destroy the object. Is it possible to transfer ownership on the QML-side? -
Thanks for your reply!
The object is created on the heap in C++. I'd prefer a solution that would not require to have it handled by JavaScript.
My use-case is this: The object wraps a task. If it fails the user is prompted (by a QML MessageDialog) whether there should be a retry (using the same object) or whether it should be canceled. In case of canceling I need to destroy the object. Is it possible to transfer ownership on the QML-side?@_antipattern_ said:
I'd prefer a solution that would not require to have it handled by JavaScript.
Is it possible to transfer ownership on the QML-side?
before that you have to decide what you want.
-
wrote on 2 Aug 2016, 15:32 last edited by
I'd like a way to prevent a memory leak. If possible I'd be great to be able to call the descructor, if not transfering the resposnibility to the GC is also fine.
-
I'd like a way to prevent a memory leak. If possible I'd be great to be able to call the descructor, if not transfering the resposnibility to the GC is also fine.
@_antipattern_
than @jeremy_k gave you the solution already -
wrote on 2 Aug 2016, 22:16 last edited by
Explicitly managing the lifetime of a C++ object used by QML is possible, but be aware that extra code is required on the QML side to detect invalid objects. Failure to do so can lead to javascript exceptions, application crashes, and the general variability of undefined behavior through the access of stray pointers.
Things such as:
property int prop: object.value
become
property int prop: isValid(object) ? object.value : -1
-
wrote on 3 Aug 2016, 12:01 last edited by
I have trouble with both solutions. In the documentation I only found how to change the ownership of an object from C++ and not from JavaScript. Is this the only way?
Also, I didn't manage to explicitly manage the object's life-cycle, does the ownership need to be transfered first? I tried using the JavaScript delete.
-
I have trouble with both solutions. In the documentation I only found how to change the ownership of an object from C++ and not from JavaScript. Is this the only way?
Also, I didn't manage to explicitly manage the object's life-cycle, does the ownership need to be transfered first? I tried using the JavaScript delete.
wrote on 3 Aug 2016, 22:04 last edited byThere isn't an API to explicitly change the ownership from javascript.
The documentation briefly covers the topic at http://doc.qt.io/qt-5/qtqml-cppintegration-data.html#data-ownership.
Is the code sufficiently brief to post here?
-
wrote on 4 Aug 2016, 11:16 last edited by _antipattern_ 8 Apr 2016, 11:16
Here are the important bits of my code:
main.qml// ... EntryModel { id: entryModel onError: { jobFailedDialog.informativeText = errorMessage jobFailedDialog.accepted.disconnect(failedJob.retry) // workaround for missing Qt::UniqueConnection in QML jobFailedDialog.accepted.connect(failedJob.retry) jobFailedDialog.rejected.disconnect(failedJob.deleteLater) jobFailedDialog.rejected.connect(failedJob.deleteLater) jobFailedDialog.open() } } MessageDialog { id: jobFailedDialog // ... standardButtons: StandardButton.Retry | StandardButton.Cancel }
EntryModel:
class EntryModel : public QAbstractListModel { // ... signals: void error(Job *failedJob, QString errorMessage); public slots: void gatewayError(std::string error_string) { qWarning() << "Error: " << QString::fromStdString(error_string); emit error(static_cast<Job*>(QObject::sender()), QString::fromStdString(error_string)); } // ... };
Job:
class Job : public QObject { // ... signals: void error(const std::string &error_string); public slots: void retry() { if (executed_) { executed_ = false; execute(); } } // ... };
The jobs are executed from the EntryModel and their error-signal is connected to EntryModel::gatewayError. This signal triggers the jobFailedDialog.
I left out the irrelevant bits, I hope the code is sufficient to clarify my problem.
1/10