管理在Signals中传递给QML的C++ QObject的生命周期

der*_*erM 3 c++ qt qml qt5 qtquick2

TL; DR
如何正确传递信息,QObject将QML 包装成可能以高频率发出的信号,减少开销,确保对象/引用至少超过连接插槽的执行?


我有一个C++ QObject注册为QML类型.这个对象有一些信号

void someSignal(InformationQObject* someInformation)
Run Code Online (Sandbox Code Playgroud)

其中我没有在单独的参数中传递所有信息但是在一个对象中 - 类似于在例如信号中找到MouseArea的信号

void clicked(QQuickMouseEvent *mouse)
Run Code Online (Sandbox Code Playgroud)

现在我想知道正确的终身管理someInformation.

到目前为止,在我的目标中,我有一个成员:

InformationQObject* m_lastInformation
Run Code Online (Sandbox Code Playgroud)

并发送我使用的信号:

void sendMySignal(/* possible params */)
{
    delete m_lastInformation
    m_lastInformation = new InformationQObject(/* right params here */)
    emit someSignal(m_lastInformation)
}
Run Code Online (Sandbox Code Playgroud)

现在这似乎是错的.

理由:如果你看一下QQuickMouseArea它们的执行情况,就会采用不同的方式.看起来他们并没有为每个事件创建一个新对象,而是回收现有事件.我发现很难找到他们的所有来源,但我认为他们的一个文件中的这个评论给出了一个很好的理由:

QQuickPointerEvent用作长期存在的对象,用于在事件传递期间存储与指点设备(例如鼠标,触摸或平板电脑事件)中的事件相关的数据.它还提供了稍后可用于将事件暴露给QML的属性,与QQuickMouseEvent,QQuickTouchPoint,QQuickKeyEvent等相同.由于一次只能传递一个事件,因此该类实际上是单例.我们不担心QObject开销,因为实例是长期的:我们不会为每个事件动态创建和销毁此类型的对象.

但这是让我看到它变得复杂的地方,他们是如何做到的.这个评论是关于a QQuickPointerEvent.有一个QQuickPointerMouseEvent.在他们的信号中他们通过了QQuickMouseEvent*

后者是指向其中一个成员的指针QQuickMouseEvent quickMouseEvent.

在某些时候,不知何故,这个指针在QML中变得无效

MouseArea {
    anchors.fill: parent
    property var firstEvent
    onClicked: {
        if (firstEvent === undefined) firstEvent = mouse
        console.log(mouse.x, mouse.y)
        console.log(firstEvent.x, firstEvent.y) // -> TypeError on second and consecutive clicks.
    }
}
Run Code Online (Sandbox Code Playgroud)

所以必定会有一些神奇的事情发生,我不明白.

dte*_*ech 5

你正在打开一堆蠕虫.QML生命周期管理在上述方案中被打破,并且API并没有真正为您提供一种有意义的方法来解决这个问题.我的解决方案是将所有权设置为CPP并手动管理对象的生命周期.原始我知道,但唯一的解决方案是避免删除仍在使用的对象和实际的硬崩溃.

如果鼠标区域循环使用相同的事件对象,则在后续单击时不会变为无效.

如果您的代码反映了您的实际使用情况,我建议您只复制单个事件属性,而不是尝试将实际事件存储在专用属性中,或者作为JS对象存储,如果您想避免开销并且不需要通知.我倾向于使用数组,并依赖更快的索引访问.

我建议的另一个解决方案是Q_GADGET使用PIMPL - 小工具受设计限制,因此它们不能作为指针传递,并且它们总是按值复制,但是您可以让实际对象只包含指向较重数据实现的指针,并且仅用作访问器和接口以从QML访问数据.这样你就可以重用数据,实际的对象值可以忽略不计,因为它基本上只是一个指针而且不涉及任何动态内存分配.您还可以将实际数据公开为不透明对象,以便将其复制到其他小工具并使用ref count来管理数据生命周期.

  • 两者都可以很好地适合你,这取决于你需要完成什么.如果信号发送器是持久的,那么如果它存储了那些东西就会非常干净和高效,除了因为你已经有了它的引用来建立连接,你甚至不需要在信号中传递额外的东西,就像你一样可以简单地使用发件人引用来检索该信息.当你需要通过"伪"引用有效地传递大对象时,gadget + pimpl非常有用,同时仍然避免了`QObject`的开销,同时保留了属性和可调用函数的基本元. (3认同)