2015-03-06 21 views
8

Myślałem, że QML obsługuje funkcje lambda ze względu na obsługę anonimowych funkcji JavaScript oraz fakt, że funkcje są obiektami pierwszej klasy, ale nie działają tak, jak się spodziewałem. Wziąć ten kod:QML: Funkcja Lambda działa nieoczekiwanie

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; 
       } 
      } 
     } 
    } 
} 

Próbuję użyć lambda function() { handler(item); } tak, że gdy sygnał jest emitowany someObj::someValueChanged element emitujący jest przekazywany do funkcji handler(item).

I zakłada, że ​​każda pętla by utworzyć nową instancję lambda, a odniesienie item wiązałoby się odniesienie do przykładu someObj utworzonego w pętli (tj item byłyby wychwytywane przez lambda). Ale to nie wydaje się być w przypadku, gdy wyjście jest:

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) 

Jak widać, zarówno cała funkcja jest zastępowana na każdej pętli lub tylko odniesienie item, tak że ostatecznie tylko ostatni stworzył someObj jest określona. Czy ktoś może mi wyjaśnić, dlaczego lambdy (jeśli to nawet to, co jest) nie działają tak, jak się spodziewam? A czy to jest problem z QML, czy ogólny kod JavaScript?

Odpowiedz

11

spróbować czegoś takiego:

item.someValueChanged.connect(function(temp) { 
    return function() { 
     handler(temp)} 
}(item)) 

Intuicyjny, prawda? : D

Jeśli JS użył "zakresu bloków", do każdej iteracji pętli odwołanoby się 3 różne item, które "działałyby zgodnie z oczekiwaniami". Ale w przypadku "zakresu funkcji" istnieje tylko jeden odnośnik, który odwołuje się do jego wartości końcowej, a zatem potrzeba użycia tego hacka do "przechwytywania" każdej wartości w czasie.

+0

Ach, to ma sens (w każdym razie dla JS). Dziękuję Ci! – cmannett85

+0

@ cmannett85 - co by się stało w C++, jeśli spróbujesz uruchomić opóźnioną lambdę, odwołując się do locals funkcji, która wykracza poza zakres? Najlepszy przypadek, jeśli na początku był na stosie, a stos nie poszedł na tę głębokość, a pamięć nie została nadpisana, może działać, ale najprawdopodobniej ulegnie awarii. – dtech

+0

Prawda, z wyjątkiem tego, że w C++ można określić, co jest przechwytywane _i jak_. Coś, czego JS nie obsługuje (przynajmniej wersja używana przez QtQuick). – cmannett85

Powiązane problemy