如何分析“绑定循环”

Posted

技术标签:

【中文标题】如何分析“绑定循环”【英文标题】:How to analyse a "Binding loop" 【发布时间】:2016-07-05 06:33:28 【问题描述】:

我有一个带有 C++ 模型和 QML 可视化的 Qt/QML 应用程序。

在运行时(启动),我收到警告

QML 项目:检测到属性“xyz”的绑定循环

我在 QML 中没有看到明显的循环。 我可以启用更多调试以了解此循环的来源吗?其他建议?

【问题讨论】:

我不确定,但 IIRC qml 分析器可以让您查看绑定调用。您还可以在属性分配中添加控制台调试输出。您可以在此处发布您的代码,以便我们为您提供帮助:) 感谢您的意见。 QML 分析器确实显示了 QML 绑定调用。但是,afaics,它没有显示循环中涉及哪些底层 C++ 代码。我曾经(现在仍然)希望有一些日志表明例如。 “a 绑定到 b” “b 绑定到 c” “c 绑定到 a” 好吧,如果有这个选项,我不知道。但这就是我有时用来调试循环的方法:height: console.debug("binding height"); return parent.height; 。请注意,这不会创建绑定本身,您必须使用 Qt.binding... doc.qt.io/qt-5/qtqml-syntax-propertybinding.html 这里有一个关于绑定循环以及如何避免它们的精彩视频:youtube.com/watch?v=aSMEcAmcPDc 【参考方案1】:

我通常通过在打印警告的 Qt 代码中放置一个断点来做到这一点。为此,您需要一个带有调试符号的 Qt。

在 Qt 源代码中搜索“检测到绑定循环”会给我QQmlAbstractBinding::printBindingLoopError()。在此处放置断点通常会导致回溯,从而清楚地了解情况。

更新:David Edmundson 开发了一个小工具,可以在绑定循环上显示仅 QML 的回溯,请参阅 his blog here。引擎盖下的内容与此处描述的完全一样,只是它很好地自动化并包装在 Python 脚本中。

例子:

Rectangle 
    id: parent
    width: child.width + 1
    height: child.height + 1
    Rectangle 
        id: child
        anchors.fill: parent
    

回溯:

1   QQmlAbstractBinding::printBindingLoopError  qqmlabstractbinding.cpp 178 0x7ffff6eb36da  
2   QQmlBinding::update qqmlbinding.cpp 221 0x7ffff6eb9abe  
3   QQmlBinding::update qqmlbinding_p.h 97  0x7ffff6eba354  
4   QQmlBinding::expressionChanged  qqmlbinding.cpp 260 0x7ffff6eb9e68  
5   QQmljavascriptExpressionGuard_callback  qqmljavascriptexpression.cpp    361 0x7ffff6eb223e  
6   QQmlNotifier::emitNotify    qqmlnotifier.cpp    94  0x7ffff6e9087a  
7   QQmlData::signalEmitted qqmlengine.cpp  763 0x7ffff6e19a45  
8   QMetaObject::activate   qobject.cpp 3599    0x7ffff683655e  
9   QMetaObject::activate   qobject.cpp 3578    0x7ffff6836364  
10  QQuickItem::widthChanged    moc_qquickitem.cpp  1104    0x7ffff7a7ba49  
11  QQuickItem::geometryChanged qquickitem.cpp  3533    0x7ffff7a6e9cd  
12  QQuickItem::setSize qquickitem.cpp  6389    0x7ffff7a75f35  
13  QQuickAnchorsPrivate::setItemSize   qquickanchors.cpp   400 0x7ffff7a60d94  
14  QQuickAnchorsPrivate::fillChanged   qquickanchors.cpp   177 0x7ffff7a5fe0e  
15  QQuickAnchorsPrivate::itemGeometryChanged   qquickanchors.cpp   441 0x7ffff7a6106f  
16  QQuickItem::geometryChanged qquickitem.cpp  3523    0x7ffff7a6e96c  
17  QQuickItem::setWidth    qquickitem.cpp  6091    0x7ffff7a74c1d  
18  QQuickItem::qt_static_metacall  moc_qquickitem.cpp  874 0x7ffff7a7b0dc  
19  QQuickItem::qt_metacall moc_qquickitem.cpp  946 0x7ffff7a7b4d8  
20  QQuickRectangle::qt_metacall    moc_qquickrectangle_p.cpp   610 0x7ffff7c189c2  
21  QMetaObject::metacall   qmetaobject.cpp 296 0x7ffff680118b  
22  QQmlPropertyPrivate::writeBinding   qqmlproperty.cpp    1512    0x7ffff6e33ec3  
23  QQmlBinding::update qqmlbinding.cpp 199 0x7ffff6eb992a  
24  QQmlBinding::update qqmlbinding_p.h 97  0x7ffff6eba354  
25  QQmlBinding::expressionChanged  qqmlbinding.cpp 260 0x7ffff6eb9e68  
26  QQmlJavaScriptExpressionGuard_callback  qqmljavascriptexpression.cpp    361 0x7ffff6eb223e  
27  QQmlNotifier::emitNotify    qqmlnotifier.cpp    94  0x7ffff6e9087a  
28  QQmlData::signalEmitted qqmlengine.cpp  763 0x7ffff6e19a45  
29  QMetaObject::activate   qobject.cpp 3599    0x7ffff683655e  
30  QMetaObject::activate   qobject.cpp 3578    0x7ffff6836364  
31  QQuickItem::widthChanged    moc_qquickitem.cpp  1104    0x7ffff7a7ba49  
32  QQuickItem::geometryChanged qquickitem.cpp  3533    0x7ffff7a6e9cd  
33  QQuickItem::setSize qquickitem.cpp  6389    0x7ffff7a75f35  
34  QQuickAnchorsPrivate::setItemSize   qquickanchors.cpp   400 0x7ffff7a60d94  
35  QQuickAnchorsPrivate::fillChanged   qquickanchors.cpp   177 0x7ffff7a5fe0e  
36  QQuickAnchorsPrivate::update    qquickanchors.cpp   431 0x7ffff7a60fc6  
37  QQuickAnchorsPrivate::updateOnComplete  qquickanchors.cpp   425 0x7ffff7a60f93  
38  QQuickItem::componentComplete   qquickitem.cpp  4593    0x7ffff7a70944  
39  QQmlObjectCreator::finalize qqmlobjectcreator.cpp   1207    0x7ffff6ecab66  
40  QQmlComponentPrivate::complete  qqmlcomponent.cpp   928 0x7ffff6e38609  
41  QQmlComponentPrivate::completeCreate    qqmlcomponent.cpp   964 0x7ffff6e386ee  
42  QQmlComponent::completeCreate   qqmlcomponent.cpp   957 0x7ffff6e386a0  
43  QQmlComponent::create   qqmlcomponent.cpp   791 0x7ffff6e37edd  
44  QQuickView::continueExecute qquickview.cpp  476 0x7ffff7b720d4  
45  QQuickViewPrivate::execute  qquickview.cpp  124 0x7ffff7b7101f  
46  QQuickView::setSource   qquickview.cpp  253 0x7ffff7b71426  
47  main    main.cpp    24  0x4033e4    

在回溯中,可以看到子项的anchors.fill 锚点是在加载文件时计算的(第35、36 帧)。这会导致子项的宽度发生变化(第 31 帧),从而导致对父项的“宽度”属性(第 17 帧)的绑定进行绑定更新(第 25 帧)。这反过来又会强制重新计算子锚点(第 14 帧),这会改变子锚点的宽度(第 10 帧),从而更新绑定(第 4 帧)。这与第 25 帧中已更新的绑定相同,因此存在绑定循环。可以看到第 25 帧和第 4 帧中的this 指针是相同的,即递归更新相同的绑定。

【讨论】:

这看起来很有希望!您能指导我如何获得“带有调试符号的 Qt”吗?我应该自己编译 Qt 吗? 如果您使用的是 Linux,请安装 *-dbg 软件包。 我自己从源代码编译 Qt,所以我只需将 -debug 标志传递给 configure。不知道预构建的包。 bugreports.qt.io 是否有功能请求默认启用有用的调试打印输出?我很想为它投票,但我的 google-foo 不能胜任这项任务。 @Velkan ,是否愿意为正确解决的问题投票? bugreports.qt.io/browse/QTBUG-36525【参考方案2】:

非常感谢您的收据,但它对我没有帮助。如果有人需要它,请添加另一种可能的解决方案。我在ListView 中得到绑定循环,试图将所有项目宽度和列表宽度设置为项目最大值:

ListView 
    implicitWidth: contentItem.childrenRect.width
    delegate: listItem


Item 
    id: listItem
    width: Math.max(internalWidth, listView.implicitWidth)

在项目计数更新时出现绑定循环错误,但并非每次都出现 - 仅在某些批量绑定更新时出现,而没有实际的绑定循环。能够通过将绑定表达式移动到 Binding QML Type 并向其添加 delayed 属性来解决该问题:

Item  // Item causing binding loop
    Binding on item_property_causing_loop 
        value: <binding_expression>
        when: <when_expression> // Optional however could also help
        delayed: true  // Prevent intermediary values from being assigned
    

所以在我的情况下是:

Item  // Item causing binding loop
    id: listItem
    Binding on width 
        value: Math.max(internalWidth, listView.implicitWidth)
        when: index >= 0 // Optional however could also help
        delayed: true    // Prevent intermediary values from being assigned
    

【讨论】:

以上是关于如何分析“绑定循环”的主要内容,如果未能解决你的问题,请参考以下文章

标签上的绑定循环警告

如何做好用户分析

QML ListView:检测到属性“高度”的绑定循环

误差分析是什么?如何进行误差分析?分析为了获得什么知识?

HR招聘要分析哪些数据,如何分析,有

如何使用JVisualVM进行性能分析