0

Do not fear! This is not production code. It is just to learn new things about QML! I do not seek 'you shall not do something like this - do it like that.' I am more interested in the internals of QML

Consider the following QML-Code

import QtQuick 2.4
import QtQuick.Window 2.0

Window {
    id: root
    width: 800
    height: 600
    visible: true
    GridView {
        width: 800
        height: 200
        model: 4000
        flow: GridView.FlowTopToBottom

        delegate: Rectangle {
            id: myDelegate
            width: 100
            height: 100
            border.width: 1
            Column {
                anchors.fill: parent
                Text {
                    text: index
                    height: 20
                    width: parent.width
                }
                Item {
                    id: frame0
                    height: 20
                    width: parent.width
                }
                Item {
                    id: frame1
                    height: 20
                    width: parent.width
                }
            }

            Component.onCompleted: {
                // if (index % 100 === 0) gc()
                frame0.children = [myComp.createObject(myDelegate)]
                frame1.children = [myComp.createObject(null)]
                frame0.children[0].text = 'QML ' + index

                frame1.children[0].text = 'JS ' + index
            }
        }

        Component {
            id: myComp
            Text {
                anchors.centerIn: parent
                Component.onDestruction: console.log('Destroy ' + text)
            }
        }
    }
}

It illustrates to some extent the MemoryManagement of QML when using dynamic ObjectCreation (JS). I have a ListView, that creates a few delegates, and lets me browse through them, creating new one on demand.

The trick is: When ever a new delegate is created, it uses the JavaScript Dynamic Object Creation to create two instances of an text-object.

One of them is parented to the delegate, the other one is parented to null and it's life is therefore determined by the JS-Engine. It should be garbage collected, once there is no pointer left, pointing towards it.

For the start, I will put both of them in a frame (Item), to display (setting a visual parent). As the delegate, those frames are destroyed. This will - as expected - destroy the dynamically created Object that has the delegate as parent, as well. The other one is (should be) left for the Garbage Collector to do his work.

And this is where it fails - sometimes the application crashes before the GC kicks in, sometimes it crashes, while the GC is trying to do its work.

Though it is not reccommended by the documentation, it does help, to call the GC manualy (activate the line commented out in Component.onCompleted).

So it seems to me, the GC overestimates it's abilities, and decides to kick in, when it is already to late.

What might be the reason for this? Is there a way to tell the GC to be mor proactive?

Again: I do not intend to use the Dynamic Object Creation with .createObject(null) in my code. It is pure curiosity.

  • Have you tried to do this when setting the newly created object into a normal property? My guess would be that the `children` property is different as it has different semantics – Kevin Krammer Nov 19 '16 at 10:42
  • It should not matter, ref counting should work in any case. – dtech Nov 19 '16 at 13:55
  • @ddriver my guess is that the QML engine expects the object to be under script ownership because it has no parent, but is actually under C++ ownership, i.e. deleted by `children`. Hence the recommendation to try with a single object property, to check if this is just the usual "no parent and also not marked for C++ ownership but deleted in C++" problem – Kevin Krammer Nov 21 '16 at 09:49

1 Answers1

4

What might be the reason for this? Is there a way to tell the GC to be mor proactive?

The reason for this is the buggy qtquick object lifetime implementation. At this point it doesn't look to be a JS thing, but a qtquick thing. It clearly doesn't abide to its own alleged rules - for example, it will delete an object with a parent while still in use, resulting in a hard crash. You cannot expect reference counting to work as well. This behavior can occur in a number of scenarios that employ dynamism, it generally does not manifest in trivial and static scenarios.

The solution, as already outlined in the linked question, is to use manual object lifetime management. Use a set of new functions for the creation and deletion of objects.

  • for creation, you must pass the object to the C++ side in order to call QQmlEngine::setObjectOwnership(ojb, QQmlEngine::CppOwnership); on it, this basically tells qtquick "don't even bother trying", luckily at least this works as it is supposed
  • for destruction, you must pass the object to the C++ side in order to call obj->deleteLater();

For me this does the trick, I no longer get crashes for no apparent reason. Use the custom lifetime management and stay away from the stock functions for that. It gives you guarantees, that the object will stay alive as long as you need it, but also that it will not stay past the point where you want it gone, which is another problem, albeit not that severe. Of course, this eliminates the convenience factor of using JS, as you have to give up on automatic lifetime management and be a little more diligent and explicit with your own code, but there isn't much you can do about it. Even though the bug was reported almost a year ago and deemed critical, not a shred of work has been done about it whatsoever. Thus I assume that as critical as it may be in its severity, it is more like lowest priority when it comes to finding its cause and fixing it.

Community
  • 1
  • 1
dtech
  • 47,916
  • 17
  • 112
  • 190
  • The bug report you've linked to seems to assume that the parent item is also the QObject parent. I.e. it checks for the QML `parent` identifier/reference, which is the parent item, not the QObject parent. Maybe the items in question don't even have a QObject parent? – Kevin Krammer Nov 19 '16 at 10:47
  • @KevinKrammer - it is just for the sake of the example. In my production code the objects that are being deleted are non-visible `QObject` derived types which interface proper parenting to QML. They get deleted with a valid parent and with multiple references to them. It is most definitely broken. – dtech Nov 21 '16 at 00:09
  • ok, but that wasn't the case in the bug report. It only checked to the `parent` property which is the parent item, not the QObject parent. – Kevin Krammer Nov 21 '16 at 09:41
  • @KevinKrammer you are beginning to sound like a Qt apologist. Maybe if you took the time to research before you rush to deny its flaws and bugs, you'd realize that using `createObject(validParentItem)` sets both the `parent` and `parentItem` to the same object, so no, **maybe it doesn't**. Also, a little extra through, and you'd realize I only used `Item` for the example to keep it simple and clear without extra user code, I would have used a bare `QtObject` if not for various other half-baked aspects of Qt such as not interfacing it's already available parent interface to qml... – dtech Nov 21 '16 at 14:11
  • I haven't looked into the implementation of createObject() it might or might not set the QObject parent. I was just saying that the bug report did not contain a check for the QObject parent. Especially since I agree that this is what one would expect, so I think it is important to point out that it is not. I learned that when working with `Repeater` as it is the QObject parent of its delegates but its parent is the parent item – Kevin Krammer Nov 22 '16 at 13:44
  • Again, that bug report example uses what qml provides, `Item` has `parent` `QtObject` has **nothing**. The example is only as good as qml allows it. In some cases the `parent` would be a different object from the `parentItem`, which is why I used `validParentItem`, but anyway, the `QObject` `parent` is always set to something when an object is created dynamically, **even when no visual parent is provided** in which case the object will be parented to the `QQuickRootItem`. **It is impossible to create an object without a `QObject` parent or even set it to null from qml**. So what's your point? – dtech Nov 22 '16 at 13:54
  • To repeat what I already wrote three times:`parent` in QML refers to the parent item. It is not necessarily referring to the object's QObject parent. I am not saying that any object doesn't have a parent object or that it is incidentally the same as the parent item, again, just saying that `parent` is *not* the parent object. Which I found confusing the first time I encountered it with `Repeater` so I point this out when appears that someone has not discovered that yet – Kevin Krammer Nov 22 '16 at 14:16
  • You are apparently completely lost, so let me digress with this final attempt to help you understand the situation. You went on to undermine a serious flaw in Qt, deemed a critical bug, on the presumption that the code in the bug reproduction doesn't check for a null parent, which is impossible to begin with. And you continue to do so, repeating the same irrelevant stuff even after you've been proven that the `QObject * parent` is always set to a valid object and your point was invalid from the very beginning. Honestly, does shilling get you extra points at work or something? Not cool.. at all – dtech Nov 22 '16 at 14:22
  • The bug report doesn't assume anything, you are the one assuming things, which have all been proven incorrect. In your position, I'd simply apologize for that stubbornness and admit I was wrong, as any reasonably intelligent person would do. – dtech Nov 22 '16 at 14:28
  • I was just pointing out that this is not the case for those who didn't know there was a difference. If you want to believe otherwise that's your choice. I assume that having a non-null QObject parent was an important thing for the bug report, so it could have done that instead of making it look like it did and relying on way of creation to make it an assumption. – Kevin Krammer Nov 23 '16 at 10:47
  • @KevinKrammer - is there anything in particular stopping you from improving on the bug report? – dtech Nov 27 '16 at 16:29