QML:Lambda 函数意外工作
Posted
技术标签:
【中文标题】QML:Lambda 函数意外工作【英文标题】:QML: Lambda function works unexpectedly 【发布时间】:2015-03-06 12:11:53 【问题描述】:我认为 QML 支持 lambda 函数是因为 javascript 支持匿名函数以及函数是一流对象的事实,但它们并没有按我的预期工作。拿这个代码:
Item
property var items: []
function handler( item )
console.log( item );
Component.onCompleted:
for ( var i = 0; i < 3; ++i )
var item = someObj.createObject();
item.someValueChanged.connect( function()
handler( item ); );
items.push( item );
console.log( "Adding:", item );
Component
id: someObj
Item
property bool someValue: false
Timer
running: true
onTriggered:
parent.someValue = true;
我正在尝试使用 lambda function() handler( item );
,以便在发出 someObj::someValueChanged
信号时,将发射项目传递给 handler( item )
函数。
我假设每个循环都会创建一个新的 lambda 实例,并且 item
引用将携带在该循环中创建的 someObj
实例的引用(即 item
将被 lambda 捕获)。但情况似乎并非如此,因为输出是:
qml: Adding: QQuickItem_QML_1(0x2442aa0)
qml: Adding: QQuickItem_QML_1(0x2443c00)
qml: Adding: QQuickItem_QML_1(0x2445370)
qml: QQuickItem_QML_1(0x2445370)
qml: QQuickItem_QML_1(0x2445370)
qml: QQuickItem_QML_1(0x2445370)
如您所见,要么在每个循环中替换整个函数,要么只替换 item
引用,因此最终只引用最后创建的 someObj
。有人可以向我解释为什么 lambdas(如果它就是这样的话)不能按我期望的方式工作吗?这是 QML 问题,还是一般的 JavaScript 问题?
【问题讨论】:
【参考方案1】:试试这样的:
item.someValueChanged.connect(function(capture)
return function()
handler(capture)
(item))
直观,对吧? :D
如果 JS 使用“块作用域”,则每次循环迭代都会引用 3 个不同的 item
s,它会“按预期工作”。但是对于“函数范围”,只有一个 item
被引用,并且它引用了它的最终值,因此需要使用该 hack 及时“捕获”每个值。
只是为了解释一下,如果不是很明显,信号连接到一个处理程序,该处理程序由一个函数仲裁,该函数将特定时间的参数值作为离散对象捕获,该对象用于馈送到处理程序。
希望最初的 Qt 5.12 版本将通过引入对 let
(也称为块范围变量)的支持来解决这个问题。
更新:我可以确认使用 5.12,它现在可以按预期工作:
let item = someObj.createObject(); // will produce 3 distinct obj refs
【讨论】:
啊,这是有道理的(无论如何对于 JS)。谢谢! @cmannett85 - 如果您尝试运行延迟的 lambda,引用超出范围的函数的局部变量,在 C++ 中会发生什么?最好的情况是,如果它一开始就在堆栈上,并且之后堆栈没有达到那个深度并且内存没有被覆盖,它可能会工作,但很可能会崩溃。 True,但在 C++ 中您可以指定捕获的内容以及捕获方式。 JS 不支持的东西(至少是 QtQuick 使用的版本)。 @ddriver 正是因为这样做,我遇到了严重的崩溃。在 QML javascript 中,我将匿名函数作为插槽传递给来自 C++ 对象的信号。有时在函数超出范围并且系统崩溃(至少在 ios 中)之后调用信号。您可以在here查看问题 在 javascript 中捕获工作的方式是最不直观的方式【参考方案2】:dtech's answer 解决了这个问题(谢谢!),但我们可以简化它,以便更清楚地知道到底发生了什么。 “内部”函数需要匿名,但“外部”函数不需要。对外部函数使用普通函数可以使代码更容易理解,并且更容易自我记录,因为函数具有名称。外部函数创建一个信号处理程序,因此等效代码可以是:
var signal_handler = create_signal_handler(item);
item.someValueChanged.connect(signal_handler);
...
function create_signal_handler(item)
return function() return handler(item);
【讨论】:
以上是关于QML:Lambda 函数意外工作的主要内容,如果未能解决你的问题,请参考以下文章
使用 Python 的三元运算符与 lambda 组合的意外输出