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)
所以可以看到,对象确实创建了两次。
有一些解决方法:
- 不要使用循环引用 - 但那会很无聊。
- 不要使用动态对象创建,因为它总是会产生问题 - 我可以只创建对象,然后使用
property alias obj: myObjectID
或 property QtObject obj: myObjectID
.这有一个缺点,即如果组件是可重用的,则用户无法替换该对象或仍然创建该对象,从而浪费内存。
- 如果 属性 仍然为空(未被可重用组件的用户覆盖),则在
Component.onCompleted
中分配 属性。这有一个缺点,即对象不可用,创建后会出现很多 Can't read property ... of null
错误。不过,他们希望不会破坏应用程序。
- 创建一个讨厌的绑定循环 写:
property QtObject obj: (obj ? obj : objPrototype.createObject(root))
。它会发出警告,但是来吧。这只是一个绑定循环,会被检测到并被破坏。
这些变通办法没什么用,但也许有用。
我有一个奇怪的问题,它隐藏在一个大型项目的某个地方。到目前为止,我无法在 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)
所以可以看到,对象确实创建了两次。
有一些解决方法:
- 不要使用循环引用 - 但那会很无聊。
- 不要使用动态对象创建,因为它总是会产生问题 - 我可以只创建对象,然后使用
property alias obj: myObjectID
或property QtObject obj: myObjectID
.这有一个缺点,即如果组件是可重用的,则用户无法替换该对象或仍然创建该对象,从而浪费内存。 - 如果 属性 仍然为空(未被可重用组件的用户覆盖),则在
Component.onCompleted
中分配 属性。这有一个缺点,即对象不可用,创建后会出现很多Can't read property ... of null
错误。不过,他们希望不会破坏应用程序。 - 创建一个讨厌的绑定循环 写:
property QtObject obj: (obj ? obj : objPrototype.createObject(root))
。它会发出警告,但是来吧。这只是一个绑定循环,会被检测到并被破坏。
这些变通办法没什么用,但也许有用。