Qt QML中值的双重初始化

Qt QML中值的双重初始化,qt,qml,qtquick2,qt5.7,Qt,Qml,Qtquick2,Qt5.7,我有一个奇怪的问题,它埋在一个大项目的某个地方。到目前为止,我还无法在一个MCVE中复制它,但一旦成功,我将把它交上来 这是一个很简单的错误行为。基本上,我有一个带有属性的QtObject,我用初始值设置如下: TestObj.qml QtObject { id: root property int val1: { console.log('set val', root); return 42 } Component.onCompleted: console.log('C

我有一个奇怪的问题,它埋在一个大项目的某个地方。到目前为止,我还无法在一个MCVE中复制它,但一旦成功,我将把它交上来

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

TestObj.qml

QtObject {
    id: root
    property int val1: { console.log('set val', root); return 42 }
    Component.onCompleted: console.log('Constructed Object', this)
}
import QtQuick 2.0
QtObject {
    property Item paps
    property int myVal: { console.log('myVal'); paps.val }
}
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
    }
}
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)
        }
    }
}
import QtQuick 2.0
QtObject {
    property Item paps
    property QtObject myObj: paps.obj

    Component.onCompleted: console.log(myObj)
}
!!!在这个例子中,我不会重现错误

我的项目的输出现在是:

set val TestObj\u QMLTYPE\u 44(0x33799fa8)

set val TestObj\u QMLTYPE\u 44(0x33799fa8)

构造对象TestObj\u QMLTYPE\u 44(0x33799fa8)

因此,尽管只创建一次对象,但初始属性分配会执行两次。
由于我不知道在哪里寻找罪犯,我无法提供一个可复制的例子,但可能有人已经偶然发现了相同的情况并找到了解决方案

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

也许他们能找到一种不需要黑客手段就能解决这个问题的方法

问题在于循环引用:

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

TestObj1.qml

QtObject {
    id: root
    property int val1: { console.log('set val', root); return 42 }
    Component.onCompleted: console.log('Constructed Object', this)
}
import QtQuick 2.0
QtObject {
    property Item paps
    property int myVal: { console.log('myVal'); paps.val }
}
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
    }
}
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)
        }
    }
}
import QtQuick 2.0
QtObject {
    property Item paps
    property QtObject myObj: paps.obj

    Component.onCompleted: console.log(myObj)
}
TestObj2.qml

QtObject {
    id: root
    property int val1: { console.log('set val', root); return 42 }
    Component.onCompleted: console.log('Constructed Object', this)
}
import QtQuick 2.0
QtObject {
    property Item paps
    property int myVal: { console.log('myVal'); paps.val }
}
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
    }
}
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)
        }
    }
}
import QtQuick 2.0
QtObject {
    property Item paps
    property QtObject myObj: paps.obj

    Component.onCompleted: console.log(myObj)
}
结果:

qml:myVal

qml:set val TestObj_QMLTYPE_4(0x2c0bafb0)

qml:set val TestObj_QMLTYPE_4(0x2c0bafb0)

qml:构造对象

可能的原因是,
{console.log('set val',root);return 42}
语句在allready分配给
myVal
时未被处理,因此该语句被执行两次

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

TestObj3.qml

QtObject {
    id: root
    property int val1: { console.log('set val', root); return 42 }
    Component.onCompleted: console.log('Constructed Object', this)
}
import QtQuick 2.0
QtObject {
    property Item paps
    property int myVal: { console.log('myVal'); paps.val }
}
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
    }
}
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)
        }
    }
}
import QtQuick 2.0
QtObject {
    property Item paps
    property QtObject myObj: paps.obj

    Component.onCompleted: console.log(myObj)
}
TestObj4.qml

QtObject {
    id: root
    property int val1: { console.log('set val', root); return 42 }
    Component.onCompleted: console.log('Constructed Object', this)
}
import QtQuick 2.0
QtObject {
    property Item paps
    property int myVal: { console.log('myVal'); paps.val }
}
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
    }
}
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)
        }
    }
}
import QtQuick 2.0
QtObject {
    property Item paps
    property QtObject myObj: paps.obj

    Component.onCompleted: console.log(myObj)
}
结果:

qml:set obj

qml:PropertyObject创建的QObject(0x2c124708)

qml:set obj

qml:PropertyObject创建的QObject(0x2c1246f8)

qml:构造对象QObject(0x2c1246f8)

qml:QObject(0x2c1246f8)

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

有一些变通办法:

  • 不要使用循环引用,但这会让人觉得不舒服
  • 不要使用动态对象创建,因为它总是会产生问题-我可以只创建对象,然后使用
    属性别名obj:myObjectID
    属性QtObject obj:myObjectID
    。这样做的缺点是,如果组件是可重用的,则用户无法替换该对象,或者该对象仍在创建中,从而浪费内存
  • 如果属性仍然为空(未被可重用组件的用户覆盖),则在
    completed.onCompleted
    中分配属性。这样做的缺点是,对象不可用、无法创建以及大量的
    无法读取属性。。。将出现0个空
    错误。不过,他们希望不会破坏应用程序
  • 创建一个讨厌的绑定循环写入:
    属性qtobj:(obj?obj:objPrototype.createObject(root))
    。它会发出警告,但别这样。它只是一个绑定循环,将被检测并破坏
  • 这些变通方法没有什么好的,但可能是有用的

    也许他们能找到一种不需要黑客手段就能解决这个问题的方法

    问题在于循环引用:

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

    TestObj1.qml

    QtObject {
        id: root
        property int val1: { console.log('set val', root); return 42 }
        Component.onCompleted: console.log('Constructed Object', this)
    }
    
    import QtQuick 2.0
    QtObject {
        property Item paps
        property int myVal: { console.log('myVal'); paps.val }
    }
    
    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
        }
    }
    
    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)
            }
        }
    }
    
    import QtQuick 2.0
    QtObject {
        property Item paps
        property QtObject myObj: paps.obj
    
        Component.onCompleted: console.log(myObj)
    }
    
    TestObj2.qml

    QtObject {
        id: root
        property int val1: { console.log('set val', root); return 42 }
        Component.onCompleted: console.log('Constructed Object', this)
    }
    
    import QtQuick 2.0
    QtObject {
        property Item paps
        property int myVal: { console.log('myVal'); paps.val }
    }
    
    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
        }
    }
    
    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)
            }
        }
    }
    
    import QtQuick 2.0
    QtObject {
        property Item paps
        property QtObject myObj: paps.obj
    
        Component.onCompleted: console.log(myObj)
    }
    
    结果:

    qml:myVal

    qml:set val TestObj_QMLTYPE_4(0x2c0bafb0)

    qml:set val TestObj_QMLTYPE_4(0x2c0bafb0)

    qml:构造对象

    可能的原因是,
    {console.log('set val',root);return 42}
    语句在allready分配给
    myVal
    时未被处理,因此该语句被执行两次

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

    TestObj3.qml

    QtObject {
        id: root
        property int val1: { console.log('set val', root); return 42 }
        Component.onCompleted: console.log('Constructed Object', this)
    }
    
    import QtQuick 2.0
    QtObject {
        property Item paps
        property int myVal: { console.log('myVal'); paps.val }
    }
    
    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
        }
    }
    
    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)
            }
        }
    }
    
    import QtQuick 2.0
    QtObject {
        property Item paps
        property QtObject myObj: paps.obj
    
        Component.onCompleted: console.log(myObj)
    }
    
    TestObj4.qml

    QtObject {
        id: root
        property int val1: { console.log('set val', root); return 42 }
        Component.onCompleted: console.log('Constructed Object', this)
    }
    
    import QtQuick 2.0
    QtObject {
        property Item paps
        property int myVal: { console.log('myVal'); paps.val }
    }
    
    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
        }
    }
    
    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)
            }
        }
    }
    
    import QtQuick 2.0
    QtObject {
        property Item paps
        property QtObject myObj: paps.obj
    
        Component.onCompleted: console.log(myObj)
    }
    
    结果:

    qml:set obj

    qml:PropertyObject创建的QObject(0x2c124708)

    qml:set obj

    qml:PropertyObject创建的QObject(0x2c1246f8)

    qml:构造对象QObject(0x2c1246f8)

    qml:QObject(0x2c1246f8)

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

    有一些变通办法:

  • 不要使用循环引用,但这会让人觉得不舒服
  • 不要使用动态对象创建,因为它总是会产生问题-我可以只创建对象,然后使用
    属性别名obj:myObjectID
    属性QtObject obj:myObjectID
    。这样做的缺点是,如果组件是可重用的,则用户无法替换该对象,或者该对象仍在创建中,从而浪费内存
  • 如果属性仍然为空(未被可重用组件的用户覆盖),则在
    completed.onCompleted
    中分配属性。这样做的缺点是,对象不可用、无法创建以及大量的
    无法读取属性。。。将出现0个空
    错误。不过,他们希望不会破坏应用程序
  • 创建一个讨厌的绑定循环写入:
    属性Qt