关闭应用程序时 QQuickItem 析构函数/changeListeners 崩溃(Qt 5.6)
Posted
技术标签:
【中文标题】关闭应用程序时 QQuickItem 析构函数/changeListeners 崩溃(Qt 5.6)【英文标题】:Crash in QQuickItem destructor / changeListeners when closing application (Qt 5.6) 【发布时间】:2016-06-07 18:26:40 【问题描述】:我们有一个相当大的 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 中,释放的内存被 0xDDDDDDD 填充,每次都会触发段错误。
完整的示例应用程序可以在这里找到: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
模式是否正确,或者有更好的方法吗?声明默认属性别名时也会发生这种情况。
为了完整起见,以下是我们在应用程序中对其进行修补的方法。当内容更改时,我们将所有项目重新设置为布局项目。 A 优雅,你都会同意的。
function reparentTo(objects, newParent)
for (var i = 0; i < objects.length; i++)
qmlHelpers.qml_SetQObjectParent(objects[i], newParent)
onContentChanged: reparentTo(content, contentLayout)
【问题讨论】:
你能用一个独立的测试用例编辑这个问题吗? 对不起,我不确定我是否在关注你。这几乎是我所能做到的自包含(顶部的 8 行是整个应用程序)。如果你想要开箱即用的东西,你可以克隆存储库。 哦,好的,抱歉,我没听清这一切。谢谢! no pb,这有点令人困惑,因为它需要分成两个文件才能显示行为。 【参考方案1】:我遇到过很多次这个问题,我不认为这是一个错误,更像是一个设计限制。您获得的隐含行为越多,您拥有的控制权就越少,从而导致对象销毁和访问悬空引用的不适当顺序。
在许多情况下,当您超出“按书本”的琐碎 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
子项与属性不同。仍会收集属性,但它们不是父级。
关于实现“动态内容”的东西,我用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 模式正确,或者是 有更好的方法吗?这也发生在声明一个 默认属性别名。
这不是我最后一次调查它,但至少是几年前的事了。将对象声明为子对象实际上成为其他对象的子对象当然很好,但在当时这是不可能的,而且可能仍然不可能。因此需要涉及对象模型和列表视图的稍微更详细的解决方案。如果您调查此事并发现不同之处,请发表评论让我知道。
【讨论】:
【参考方案2】:我相信您不能在 var 中声明 Window 对象。在我的测试中,子窗口永远不会打开,有时在启动时会损坏。
一个窗口可以被声明在一个Item中,也可以在另一个Window中;在这种情况下,内部窗口将自动成为外部窗口的“瞬态” 见:http://doc.qt.io/qt-5/qml-qtquick-window-window.html
将您的代码修改为:
Window
visible: true
PFDialog
content: Text text: "Foobar"
【讨论】:
是的,这是我们想要的行为,实际上我们在主应用程序中使用 Dialog 作为一个类。我认为这不是这里的问题,确实你可以用一个简单的 Item 替换 Window,我只是想模仿我们的主应用程序。感谢您的洞察力。以上是关于关闭应用程序时 QQuickItem 析构函数/changeListeners 崩溃(Qt 5.6)的主要内容,如果未能解决你的问题,请参考以下文章