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

Man*_*ahl 11 qt qml

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

Window {
    property alias content: contentLayout.children
    ColumnLayout {
        id: contentLayout
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Window {
    visible: true
    property var window: PFDialog {
        content: Text { text: "Foobar" }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是当应用程序关闭时,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);
}
Run Code Online (Sandbox Code Playgroud)

原因是我们的对话框(上例中的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)
Run Code Online (Sandbox Code Playgroud)

dte*_*ech 5

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

有很多情况下,当你超出"书本"qml应用程序的一个微不足道的范围时,它可以"独立地"发生,但在你的情况下,你正在做它.

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

property var window: PFDialog {
    content: Text { text: "Foobar" }
}
Run Code Online (Sandbox Code Playgroud)

而是使用这个:

property Window window: dlg // if you need to access it externally
PFDialog {
    id: dlg
    content: Text { text: "Foobar" }
}
Run Code Online (Sandbox Code Playgroud)

这是一个很好的理由:

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
  }
}
Run Code Online (Sandbox Code Playgroud)

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

至于实现"动态内容"的东西,我得到了很好的结果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
    }
}
Run Code Online (Sandbox Code Playgroud)

然后:

PFDialog {
    layout: ObjectModel {
        Text { text: "Foobar" }
        // other stuff
    }
}
Run Code Online (Sandbox Code Playgroud)

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

onClosing: {
    if (!canExit) doCleanup()
    close.accepted = true
}
Run Code Online (Sandbox Code Playgroud)

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

最后:

是我们的属性别名内容:layout.children模式正确,还是有更好的方法来做到这一点?声明默认属性别名时也会发生这种情况.

这不是我最后一次调查它,但它至少在几年后.将对象声明为儿童实际上成为其他对象的孩子肯定会很好,但当时这是不可能的,但仍然可能不是.因此需要涉及对象模型和列表视图的稍微详细的解决方案.如果你调查此事并找到不同的东西,请发表评论告诉我.