我正在尝试测试QML以了解它如何与C++一起工作。我有ClassA
和ClassB
- 2 个类似的C++类。这是一个ClassA
.所有方法的名称都是不言自明的,所以我不会在这里放置实现。
class ClassB;
class ClassA : public QObject
{
Q_OBJECT
public:
explicit ClassA(QObject *parent = 0);
~ClassA();
Q_PROPERTY(ClassB* classB READ getClassB WRITE setClassB NOTIFY classBChanged)
ClassB* getClassB() const;
void setClassB(ClassB *classB);
signals:
void classBChanged();
private:
ClassB *m_classB;
};
ClassB
是一样的,只需将所有*lassA*
更改为*lassB*
,将所有*lassB*
更改为*lassA*
即可。
然后我注册了 QML 中分类的两者
qmlRegisterType<ClassA>("testmodule.test",1,0,"ClassA");
qmlRegisterType<ClassB>("testmodule.test",1,0,"ClassB");
在鼠标单击QML
代码中,我像这样创建两个对象:
onClicked: {
var comp = Qt.createComponent("TClassA.qml"); //TClassA.qml is
//a component of type
//ClassA
var ca = comp.createObject();
comp = Qt.createComponent("TClassB.qml");
var cb = comp.createObject();
ca.classB = cb;
cb.classA = ca;
parent.blockFromGC = ca;
}
之后,我用gc()
调用垃圾收集器.我预计ca
被阻止通过parent.blockFromGC
删除,cb
被阻止从ca
参考删除。但是垃圾收集器摧毁了cb
,之后parent.blockFromGC.classB === null
.
所以我对这段代码有第二个MouseArea
:
onClicked: {
console.log(mouse.button)
// if (mouse.button == Qt.RightButton) {
// console.log(parent.vasya.classB)
// }
gc();
console.log(parent.blockFromGC.classB) //I use cb here
}
因此,当我单击控制台MouseArea
时:
qml: 1 //Left button
qml: null //value of parent.blockFromGC.classB
classB destroyed: TQMLClassB(0x34960d0) //I have qDebug() in destructor
所以我的对象cb
被摧毁了。
所以我有这个问题:
1)有没有办法将C++
类型注册为基本类型,这样我就可以编写var ca = new ClassA()
而不是创建*.qml
文件,创建组件并最终创建对象?
2)为什么垃圾回收器破坏了我的cb
对象,我该怎么办防止此对象被删除?
此外!如果我取消注释那些注释行
// if (mouse.button == Qt.RightButton) {
// console.log(parent.vasya.classB)
// }
无论我按下哪个按钮,对象都不会再被破坏。
qml: 1 //left button
qml: TQMLClassB(0x3df8e90) //object is alive
.....
qml: 2 //right button
qml: TQMLClassB(0x3df8e90) //before gc() - alive
qml: TQMLClassB(0x3df8e90) //after gc() - alive
3) 在哪里可以详细了解QML
内存管理?我觉得这种行为真的很奇怪。
补充1:我更多地玩了一下这种情况,结果是不可预测的。我从 5.3 更新到 Qt 5.4,这种删除对象的行为已经消失。问题是该行为是如此不可预测,以至于我无法在Qt 5.4中重现此行为的事实并不意味着问题已解决。我将尝试查看错误报告和错误修复。如果我发现了什么,我会在这里发布。如果没有,我将尝试在Qt 5.4中重现这种情况并发布报告。
-
与任何 QML 类型一样,您可以在另一个组件中静态定义一个组件:
Component { id: classAComponent ClassA { } } onClicked { var ca = classAComponent.createObject() }
-
这里有一个微妙之处:将QML对象分配给QML
property
将增加其JavaScript引用计数。但是,仅存储在 C++ 对象Q_PROPERTY中的实例不会被垃圾回收器标记。QML实行双重所有制。首先,它定义了用于显示和所有权的 QObject/QQuickItem 的层次结构。附加到此主干的是一个垃圾收集系统,其中任何QML对象都可以通过
property var
拥有JavaScript对象树。因此,为了保持 ClassB 对象的活动状态,要么必须将其保留在 QML 属性中,要么在调用 component.createObject() 时为其提供父级(这是一个硬所有权;当父对象被销毁时,无论对它的任何 JS 引用,它都会被销毁)
QML 属性的示例:
Component { id: classAComponent ClassA { property Item refClassB } } onClicked { var ca = classAComponent.createObject() ca.refClassB = classBComponent.createObject() }
理想情况下,您应该尽可能避免动态创建对象,并像普通 QML 组件一样静态地使用 C++ 对象,并让声明式结构自动维护 QObject 主干,如下所示:
ClassA { classB: ClassB { } }
-
可悲的是,我所知道的最好的,比C++更多的是来自JavaScript的动态QML对象创建。