QML 中值的双重初始化

Double-Initialisation of values in QML

我有一个奇怪的问题,它隐藏在一个大型项目的某个地方。到目前为止,我无法在 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)

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

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

Created Bug-Report: maybe they find a way to solve this issue withouth hacky workarounds

问题是循环引用:

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

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))。它会发出警告,但是来吧。这只是一个绑定循环,会被检测到并被破坏。

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