关闭应用程序时,QQuickItem析构函数/ changeListeners崩溃(Qt 5.6)

时间:2016-06-07 18:26:41

标签: qt qml

我们有一个相当大的QtQuick应用程序,有很多模态对话框。所有这些模态共享一致的外观和行为,并具有leftButtons,rightButtons,内容和其他警告小部件。我们使用以下基类(PFDialog.qml):

Window {
    property alias content: contentLayout.children
    ColumnLayout {
        id: contentLayout
    }
}

并按以下方式声明对话框(main.qml):

Window {
    visible: true
    property var window: PFDialog {
        content: Text { text: "Foobar" }
    }
}

问题是当应用程序关闭时,QQuickItem析构函数中会发生段错误。这个段错误很难重现,但这是实现它的一种可靠方法:使用Visual Studio处于调试模式时,释放的内存中充满了0xDDDDDD,并且每次触发段错误。

可以在此处找到完整的示例应用程序:https://github.com/wesen/testWindowCrash

崩溃发生在QQuickItem::~QQuickItem

for (int ii = 0; ii < d->changeListeners.count(); ++ii) {
    QQuickAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate();
    if (anchor)
        anchor->clearItem(this);
}

原因是我们的对话框(上例中的Text项)的内容是主窗口的QObject子项,但是是对话框窗口的可视子项。关闭应用程序时,首先销毁对话框窗口,并且在删除Text项时,对话框窗口(仍然注册为changeListener)是陈旧的。

现在我的问题是:

  • 这是一个QtQuick错误吗?该对话框是否应该在销毁时将其自身注销为其子项的changeListener(我认为应该这样)
  • 是我们的property alias content: layout.children模式正确,还是有更好的方法来做到这一点?声明默认属性别名时也会发生这种情况。

为了完整起见,以下是我们在应用程序中修补它的方法。当内容更改时,我们将所有项目重新显示到布局项目。一种优雅,你们都会同意。

function reparentTo(objects, newParent) {
    for (var i = 0; i < objects.length; i++) {
        qmlHelpers.qml_SetQObjectParent(objects[i], newParent)
    }
}
onContentChanged: reparentTo(content, contentLayout)

2 个答案:

答案 0 :(得分:5)

我有很多次这个问题,我不认为这是一个错误,更像是设计限制。您获得的隐式行为越多,您拥有的控制就越少,导致不适当的对象销毁订单和对悬空引用的访问。

在很多情况下,这种情况可能会发生,并且会自行发生#34;因为你超越了琐碎的界限&#34;通过书&#34; qml应用程序,但在你的情况下,你是谁做的。

如果您想拥有适当的所有权,请不要使用此功能:

property var window: PFDialog {
    content: Text { text: "Foobar" }
}

而是使用它:

property Window window: dlg // if you need to access it externally
PFDialog {
    id: dlg
    content: Text { text: "Foobar" }
}

这是一个很好的理由:

property var item : Item {
  Item {
    Component.onCompleted: console.log(parent) // qml: QQuickItem(0x4ed720) - OK
  }
}
// vs
property var item : Item {
  property var i: Item {
    Component.onCompleted: console.log(parent) // qml: null - BAD
  }
}

孩子与财产不同。仍然收集属性,但它们不是父级。

至于实现动态内容&#34;事情,我ObjectModel已经取得了不错的成绩:

Window { 
    property ObjectModel layout
    ListView {            
        width: contentItem.childrenRect.width // expand to content size
        height: contentItem.childrenRect.height
        model: layout
        interactive: false // don't flick
        orientation: ListView.Vertical
    }
}

然后:

PFDialog {
    layout: ObjectModel {
        Text { text: "Foobar" }
        // other stuff
    }
}

最后,为了在关闭应用程序之前进行显式清理,在主QML文件上可以实现处理程序:

onClosing: {
    if (!canExit) doCleanup()
    close.accepted = true
}

这样可以确保在不进行清理的情况下不会破坏窗口。

最后:

  

是我们的属性别名内容:layout.children模式正确,或者是   有更好的方法吗?声明a时也会发生这种情况   默认属性别名。

上次我没有看过它,但至少在几年前它已经开始了。将对象声明为儿童实际上成为其他对象的孩子肯定会很好,但当时这是不可能的,但仍然可能不是。因此需要涉及对象模型和列表视图的稍微详细的解决方案。如果您调查此事并找到不同的内容,请发表评论告诉我。

答案 1 :(得分:2)

我相信你不能在var中声明一个Window对象。在我的测试中,SubWindow永远不会打开,有时会在启动时破坏。

  

可以在Item内或另一个Window内声明一个Window;在这种情况下,内部窗口将自动变为&#34;瞬态为&#34;外窗   请参阅:http://doc.qt.io/qt-5/qml-qtquick-window-window.html

将您的代码修改为:

Window {
    visible: true
    PFDialog {
        content: Text { text: "Foobar" }
    }
}