我习惯用参数编写我的“propertyChanged”signal
s,这样接收端就不需要显式调用Q_PROPERTY
的{{1}}函数了。
我这样做是出于清晰度和假设,在QML数据绑定情况下,不需要对getter进行“昂贵”调用来实际获取值,因为它已经作为信号参数传递给QML。 / p>
我的同事们不同意并且说它反对“QML风格”,我回复文件清楚地表明它可能有一个参数将采用潜在成员的新价值:
READ
变量的
NOTIFY
信号必须采用零个或一个参数,该参数必须与属性的类型相同。该参数将采用属性的新值。
文档中没有任何内容表明QML绑定系统使用此参数来防止在处理信号时对getter进行额外的函数调用。我理解这个调用可能是用C ++编写的,因此不会进行“昂贵的”QML到C ++调用,但它仍然是一个额外的函数调用,原则上可能会导致在许多更新时出现明显的性能损失。 / p>
我尝试检查QML绑定源代码,但无法从中推断出任何内容。我想知道是否有人知道这笔交易是什么:是否使用信号参数?
答案 0 :(得分:2)
假设在QML数据绑定情况下,没有“昂贵”的调用 需要完成getter才能实际获取值,就像它一样 已作为信号参数传递给QML。
从技术上讲,与你描述的东西不太相似。它没有意义。如果您的getter很昂贵,您应该注意以更简单的形式缓存结果,并根据更改或按需更新。
如果QML绑定专门针对单个属性的单个属性,那么这样的优化可能是有意义的。但是QML中的绑定实际上是匿名表达式,它的工作方式是当表达式引用的对象的任何更改通知触发其重新评估时。在这种情况下,它会给实现增加不必要的复杂性,从通知信号发送一个值以保留,而其他值则必须为getter调用。
显然,我只是假设在这里。让我怀疑存在这种优化的另一个问题是,属性绑定系统并不是那么复杂或者只关注效率。从属性价值相互依赖性的情况下甚至无法消除冗余评估这一事实可以看出这一点。我已经看到声称存在这样的优化,但我已经测试过,它甚至无法避免最简单的绑定树冗余。
当然,如果你坚持MEMBER
属性,这并不容易证明,因为getter是自动生成的,你不会在那里插入调试语句。
但是如果你尝试使用常规属性,你会发现即使信号发出实际值,也会调用getter。绝对没有理由为什么自动生成的吸气剂会得到不同的治疗方法。
如链接答案中所述,如果您需要为代码的C ++部分发出的值,那么请保持它不会干扰QML部分。如果没有,那么只是不发出值,即使微不足道,它仍然是开销。发送值是C ++方式,绑定是一个根本不同的概念,在C ++中并不真正适用(并非没有广泛的冗长),QML方式不需要发出更改的值。
所以你们两个都错了。它不是真的“反对QML风格”,因为它不会阻碍任何事情,并且可以选择在文档中发出一个值,但绝不表示它“符合QML风格”,或者它得到任何特别待遇。所以如果这就是你这样做的原因,你也可以停下来,因为它完全是多余的。
答案 1 :(得分:2)
我想知道是否有人知道这笔交易是什么:是否使用信号参数?
这是一种检查方式:
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QDebug>
class MyType : public QObject
{
Q_OBJECT
Q_PROPERTY(bool foo READ foo WRITE setFoo NOTIFY fooChanged)
public:
MyType(QObject *parent = nullptr) :
QObject(parent),
mFoo(0)
{
}
bool foo() const
{
qDebug() << Q_FUNC_INFO;
return mFoo;
}
void setFoo(bool foo)
{
if (foo == mFoo)
return;
mFoo = foo;
emit fooChanged(mFoo);
}
signals:
void fooChanged(bool foo);
private:
bool mFoo;
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
qmlRegisterType<MyType>("App", 1, 0, "MyType");
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}
#include "main.moc"
<强> main.qml:强>
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 2.0
import App 1.0
Window {
width: 400
height: 400
visible: true
Switch {
id: fooSwitch
}
MyType {
id: myType
foo: fooSwitch.checked
onFooChanged: print("onFooChanged, foo =", foo)
// onFooChanged: print("onFooChanged myType.foo =", myType.foo)
}
}
来回切换时的输出是:
qml: onFooChanged, foo = true
qml: onFooChanged, foo = false
因此可以安全地说使用该值而不是吸气剂。
要查看未使用signal参数的输出结果,请取消注释注释掉的行并注释掉另一行:
bool __cdecl MyType::foo(void) const
qml: onFooChanged myType.foo = true
bool __cdecl MyType::foo(void) const
qml: onFooChanged myType.foo = false
答案 2 :(得分:2)
在onPropertyChanged
- 信号中传递已更改的属性的值,尽管可能,但肯定不是 QML样式
是不是,那么你应该期望至少对于它实现的基本类型,这很容易显示,它不是。
<强> basictypes.qml 强>
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: root
visible: true
width: 400; height: 450
property int num: 5
Button {
text: num
onClicked: num += 1
}
onNumChanged: console.log(JSON.stringify(arguments), arguments.length)
}
正如您在输出中看到的那样,当您更改一个最基本的类型(例如int
)时,没有传递任何参数。
如果现在QML将使用可选但很少实现的传递值,那么会产生开销,因为在使用它之前总是需要检查参数是否存在。虽然简单的检查不是昂贵的,但如果它通常评估为false
,然后您使用解决方法,为什么事先这样做?
虽然我可能不排除,任何onPropertyChanged
中的任何传递值 - 官方现实中的信号,QML中添加property [type] [name]
的属性都没有。对于大多数继承的属性也没有(测试按钮:text
,width
,height
)。
答案 3 :(得分:0)
请注意,在QtCreator中通过右键单击属性foo
并选择“重构/生成缺少的Q_PROPERTY成员”生成的代码
Q_PROPERTY(bool foo READ foo NOTIFY fooChanged)
生成包含该属性的信号
void fooChanged(bool foo);
所以我认为这种是Qt / QML风格