【问题标题】:Double-Initialisation of values in QMLQML 中值的双重初始化
【发布时间】:2017-05-10 12:45:30
【问题描述】:

我有一个奇怪的问题,它隐藏在一个大型项目的某个地方。到目前为止,我无法在 MCVE 中重现它,但一旦成功,我就会将其提交。

这是一个非常简单的错误行为。基本上我有一个带有属性的QtObject,我使用初始值设置如下:

TestObj.qml

QtObject {
    id: root
    property int val1: { console.log('set val', root); return 42 }
    Component.onCompleted: console.log('Constructed Object', this)
}

!!! 在这个例子中,我不会重现错误 !!!

我的项目中的输出现在是:

set val TestObj_QMLTYPE_44(0x33799fa8)
set val TestObj_QMLTYPE_44(0x33799fa8)
Constructed Object TestObj_QMLTYPE_44(0x33799fa8)

因此,虽然对象只创建了一次,但初始属性分配执行了两次。
由于我不知道在哪里寻找罪魁祸首,我无法提供可重现的示例,但也许有人已经偶然发现了同样的情况并找到了解决方案。

解决方案将是有益的,因为此问题会导致某些对象的多个实例化,而我无法销毁。

【问题讨论】:

  • 如果在属性绑定中给对象一个id,然后是print(objectId),你会得到什么输出?还有QML_DISABLE_DISK_CACHE=1 的建议,它在过去有问题时会有所帮助(虽然我不知道什么时候引入的)。
  • 有效问题。我相应地编辑了问题。
  • QML_DISABLE_DISK_CACHE=1 应该在 JIT 开始生成 .qmlc-files 时在 qt5.8 中引入
  • 我认为我的发现更适合 answer-部分而不是 question-部分,尽管问题仍然存在,是否存在比提出的 4 种糟糕的解决方法更好的解决方案。

标签: qt qml qtquick2 qt5.7


【解决方案1】:

Created Bug-Report: 也许他们找到了解决这个问题的方法,而无需使用 hacky 变通方法

问题是循环引用:

循环引用会导致创建对象时出现奇怪的行为。

TestObj1.qml

import QtQuick 2.0
QtObject {
    property Item paps
    property int myVal: { console.log('myVal'); paps.val }
}

TestObj2.qml

import QtQuick 2.0    
Item {
    id: root
    property int val: { console.log('set val', root); return 42 }
    Component.onCompleted: console.log('Constructed Object')

    TestObj1 {
        id: to1
        paps: root
    }
}

结果:

qml: myVal
qml: set val TestObj_QMLTYPE_4(0x2c0bafb0)
qml: set val TestObj_QMLTYPE_4(0x2c0bafb0)
qml: Constructed Object

造成这种情况的可能原因是,语句{console.log('set val', root); return 42 } 尚未被处理,而它已经分配给myVal,因此该语句被执行了两次。

虽然这在正常情况下没有问题,但它可能会导致问题,只要我们没有在这些属性中动态创建对象。

TestObj3.qml

import QtQuick 2.0

Item {
    id: root
    property QtObject obj: { console.log('set obj'); return objPrototype.createObject(root) }
    Component.onCompleted: console.log('Constructed Object', obj)

    TestObj4 {
        id: to1
        paps: root
    }

    Component {
        id: objPrototype
        QtObject {
            id: op
            Component.onCompleted: console.log('PropertyObject created', op)
        }
    }
}

TestObj4.qml

import QtQuick 2.0
QtObject {
    property Item paps
    property QtObject myObj: paps.obj

    Component.onCompleted: console.log(myObj)
}

结果:

qml: set obj
qml: PropertyObject created QObject(0x2c124708)
qml: set obj
qml: PropertyObject created QObject(0x2c1246f8)
qml: Constructed Object QObject(0x2c1246f8)
qml: QObject(0x2c1246f8)

所以你可以看到,这个对象确实被创建了两次。

有一些解决方法:

  1. 不要使用循环引用 - 但这会很无聊。
  2. 不要使用动态对象创建,因为它总是会产生问题 - 我可以只创建对象,然后使用 property alias obj: myObjectIDproperty QtObject obj: myObjectID。这样做的缺点是,如果组件是可重用的,则用户无法替换该对象或该对象仍在创建中,从而浪费内存。
  3. 如果属性仍然为空(未被可重用组件的用户覆盖),则在 Component.onCompleted 中分配该属性。 这有一个缺点,即对象不可用,创建时和很多Can't read property ... of null 会出现错误。不过,他们希望不会破坏应用程序。
  4. 创建一个讨厌的绑定循环写:property QtObject obj: (obj ? obj : objPrototype.createObject(root))。它会发出警告,但是来吧。这只是一个绑定循环,会被检测到并被破坏。

这些变通办法没有什么好,但也许有用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-27
    • 2012-10-08
    • 2011-03-14
    • 2013-03-16
    • 1970-01-01
    • 2017-07-24
    • 2018-06-28
    • 2012-08-28
    相关资源
    最近更新 更多