[翻譯] 深入解析QML引擎, 第2部分: 綁定(Bindings)

原文 ?QML Engine Internals, Part 2: Bindings

譯者注:這個解析QML引擎的文章共4篇畜普,分析非常透徹魁兼,在國內(nèi)幾乎沒有找到類似的分析,為了便于國內(nèi)的QT/QML愛好者和工作者也能更好的學習和理解QML引擎,故將這個系列的4篇文章翻譯過來咐汞。翻譯并不是完全直譯,有不足之處儒鹿,請指正化撕,謝謝!

———————————————————————————————————————————

上一篇:QML文件加載? ? ?下一篇 綁定類型

該博文是深入解析QML引擎系列博文中的第二篇约炎。在上一篇博文中植阴,我們已經(jīng)為大家揭示了QML引擎是如何加載QML文件的。簡明扼要地回顧一下:解析QML文件圾浅,并為文件中的所有元素創(chuàng)建C ++對象掠手。例如,QML文件中包含一個Text(文本)元素狸捕,QML引擎就會創(chuàng)建一個C ++ ?QQuickText類的實例喷鸽。

QML引擎主要用于處理QML文件的加載,加載之后的運行時階段就不再那么需要它了灸拍。運行時的事件處理和繪制等都是由它生成的C++類來完成的做祝。例如,TextInput(文本輸入)元素的輸入事件是由QQuickTextInput::keyPressEven()處理鸡岗,繪制則由QQuickTextInput::updatePaintNode()實現(xiàn)混槐,完全不需要QML引擎參與。

但是在運行時轩性,QML引擎依然會涉及到兩個重要的東西:信號處理器和屬性綁定更新(譯者注:屬性綁定更新声登,其實就是計算屬性右邊表達式的值,后續(xù)有詳細的講解揣苏,這個地方不用擔心不明白悯嗓。)。比如MouseArea的一個onClicked處理器就是信號處理器(譯者注:MouseArea是第一篇博文的例子中的一個元素舒岸,從第一篇分析的內(nèi)容绅作,我們了解onClicked這種這樣的信號處理器也被看作是屬性值,和普通的屬性沒啥差別)蛾派。我們將在這篇文章中深入分析綁定(bindings)俄认。在此之前請先看下面這個例子:

圖1 QML文件例子

在這個例子中,包含了給屬性賦值的兩種方式:

1. 簡單的賦一個值洪乍,比如給QQuickRectangle的width屬性賦值300眯杏。對應的VME指令是STORE_DOUBL。它會在組件創(chuàng)建后執(zhí)行壳澳,只是簡單的調(diào)用函數(shù)QMetaObject::metacall(QMetaObject::WriteProperty,…), ?該函數(shù)最終執(zhí)行QQuickRectangle:setWidth()來設置width屬性的值岂贩。在初始化之后,QML引擎再也不會修改width屬性的值了巷波。

2. 賦一個綁定(binding)萎津,比如給text屬性賦一個綁定 "Window Area:" +(parent.width* parent.height)卸伞,給anchors.centerIn屬性賦一個綁定 parent。綁定的神奇之處在于锉屈,當Rectangle的height和width屬性改變時荤傲,會自動更新到text屬性。這是如何實現(xiàn)的呢颈渊?其實也沒那么神奇杈帐,接下來我們將為你揭曉它的運作機制她君。(譯者注:原作者說的binding到底是什么,下面馬上就會揭曉,不用擔心投储。)

創(chuàng)建綁定

通過設置QML_COMPILER_DUMP=1來輸出VME指令晌区,我們可以看到例子中的兩個綁定都是由指令STORE_COMPILED_BINDING創(chuàng)建的:

圖2 創(chuàng)建綁定的VME指令

編譯后綁定是一種優(yōu)化的綁定睹簇,我們還是先研究一下普通綁定驶悟,它是由STORE_BINDING指令創(chuàng)建的。查看QQmlVME::run()的代碼妹田,我們發(fā)現(xiàn)代碼中創(chuàng)建了一個QQmlBinding對象唬党,并把 "function $text() { return "Window Area:"+ (parent.width *parent.height) }" 做為它的表達式。沒錯鬼佣,每一個綁定都是一個JavaScript函數(shù)驶拱!"function $text()" 這部分代碼是由QML編譯器添加的,這是因為QML的JavaScript引擎V8只支持完整的函數(shù)晶衷。這個JavaScript函數(shù)緊接著會被V8編譯器編譯成一個V8::Function對象蓝纲。因為V8引擎有一個實時(JIT)編譯器,所以它會生成本地的機器碼(譯者注:傳統(tǒng)的JavaScript引擎是把JavaScript代碼先編譯為字節(jié)碼晌纫,然后再通過解釋器執(zhí)行字節(jié)碼税迷,V8引擎運用JIT技術(shù),不通過解釋器執(zhí)行字節(jié)碼锹漱,而是直接把JavaScript代碼編譯成運行在CPU(x86/x64/ARM)上的機器碼)箭养。這時,V8:: Function對象并不會被執(zhí)行哥牍,但是它會一直保留毕泌。

STORE_BINDING指令創(chuàng)建一個綁定可總結(jié)為:先創(chuàng)建了一個QQmlBinding對象,然后該對象借助V8引擎把傳給它的JavaScript函數(shù)編譯成了一個V8::Function對象嗅辣。

(譯者注:為了更容易理解后續(xù)的內(nèi)容撼泛,在這里約定“綁定”即JavaScript函數(shù),“綁定對象”即QQmlBinding對象澡谭,計算綁定的值即表示運行JavaScript函數(shù)求值愿题,或者執(zhí)行V8::Function代碼求值)

運行綁定

在某些時候,綁定需要被運行,這意味著讓V8引擎對綁定求值并將結(jié)果賦值給對應的屬性潘酗。這些都是在創(chuàng)建階段的最后階段完成的杆兵。

QQmlVME::complete()會調(diào)用每個綁定對象的update()函數(shù),在我們的例子中就是QQmlBinding:: update()函數(shù)仔夺。update()只是簡單的執(zhí)行v8:Function對象并將返回值賦給目標屬性,這在我們的例子中就是Rectangle的text屬性拧咳。

但是V8引擎是怎么知道parent.width和parent.height的值的呢?說實在的囚灼,它究竟是怎么知道parent對象的?答案就是:它不知道祭衩。V8引擎沒有任何線索知道到底存在哪些對象灶体,類名是什么,也不知道這些對象的屬性是什么掐暮。當V8引擎遇到一個未知類或未知屬性時蝎抽,它會詢問QML引擎中的一個對象包裹器(Object Wrapper),這個對象包裹器會找到正確的類或?qū)傩月房耍阉麄兎祷亟oV8引擎樟结。下面我們通過堆棧信息來看一看QQuickItem的width屬性是如何被訪問的:

圖3 屬性訪問函數(shù)調(diào)用堆棧

從上面的堆棧信息來看,我們發(fā)現(xiàn)qv8qobjectwrapper.cpp中的包裹類最終調(diào)用函數(shù)QObject::qt_metacall(QMetaObject::ReadProperty,…) 來獲取屬性值精算。首先包裹類被V8代碼調(diào)用瓢宦,然后V8代碼又被V8::Function對象對應的機器碼調(diào)用。由于機器碼沒有堆棧幀(stack frames)灰羽,因此GDB(調(diào)試工具)沒法顯示在??之后的堆棧信息驮履。在上面的堆棧信息中我做了一點點假,其實它是由兩個獨立的堆棧信息拼起來的廉嚼,細心的讀者會發(fā)現(xiàn)玫镐,堆棧幀的序號并是不連續(xù)的。

由上可知怠噪,V8引擎會使用一個對象包裹類來獲取屬性值恐似。同理,它會使用一個上下文包裹類來找到對象傍念。例如矫夷,在我們的例子中,計算綁定值的過程中需要訪問parent對象捂寿,就是通過這種方式來找到parent的口四。

綜上所述:通過運行編譯后的V8::Function代碼來對綁定進行求值,再由V8引擎通過Qt里的包裹類來訪問對象和屬性秦陋,然后將求的值賦給目標屬性蔓彩。

更新綁定

好了,現(xiàn)在我們知道text屬性是如何獲得它的初始值的。但是綁定更新是如何實現(xiàn)的赤嚼?當height和width屬性改變時旷赖,QML引擎是怎么知道需要重新對綁定求值的呢?

這個問題的答案就隱藏在對象包裹類中更卒。你應該還記得等孵,當V8引擎需要訪問一個屬性時,就會調(diào)用它蹂空。這個對象包裹類不止返回屬性值:它還會捕獲所有被訪問過的屬性俯萌。從根本上講,當一個屬性被訪問時上枕,對象包裹類會調(diào)用綁定對象的捕獲函數(shù)咐熙,在我們的例子中就是QQmlJavaScriptExpression::GuardCapture::captureProperty() ?(QQmlBinding是QQmlJavaScriptExpression的子類)。在捕獲函數(shù)內(nèi)部實現(xiàn)中辨萍,只是簡單地把綁定對象的一個槽函數(shù)連接到被捕獲屬性的NOTIFY信號棋恼。當NOTIFY信號被觸發(fā)時,與之連接的槽函數(shù)就會被調(diào)用锈玉,并重新計算綁定的值爪飘。如果你還沒有聽說過NOTIFY信號拉背,也不用擔心,這很簡單:當一個屬性用Q_PROPERTY來聲明時抡诞,在那里就可能聲明了一個NOTIFY信號昼汗。只要屬性發(fā)生改變鬼雀,擁有該屬性的對象就會觸發(fā)NOTIFY信號源哩。比如,QQuickItem的width屬性的聲明類似如下:

Q_PROPERTY(qrealwidth READ width WRITE setWidth NOTIFY widthChanged)

在我們這個例子中谓着,首次運行綁定赊锚,訪問width屬性時舷蒲,該屬性的捕獲函數(shù)將綁定對象中的一個槽函數(shù)連接到widthChanged()信號。在此之后堤框,只要QQuickItem觸發(fā)widthChanged()信號纵柿,對應的槽函數(shù)將被調(diào)用,并重新計算綁定的值资昧。

這就是為什么當你的屬性發(fā)生改變時荆忍,擁有并觸發(fā)NOTIFY信號是非常的重要撤缴。假如你忘了這樣做屈呕,綁定的值就不會被重新計算,基本上蟋软,屬性綁定就無法正確的運作嗽桩。另一方面碌冶,盡管屬性并沒有真正地改變扑庞,但你也觸發(fā)了NOTIFY信號罐氨,那么綁定的值也會被毫無意義地重新計算栅隐。

綜上所述:當訪問屬性時,對象包裹類會調(diào)用綁定對象的捕捉函數(shù)邑遏,它會將綁定對象的一個槽函數(shù)連接到該屬性的NOTIFY信號记盒,以便當屬性改變時重新計算綁定的值憎蛤。

結(jié)論

在這篇博文中,我們已經(jīng)深入分析綁定是如何工作的纪吮。總結(jié)成一句簡短的話就是:每個綁定都是一個編譯過的JavaScript函數(shù)碾盟,當任何一個引用的屬性改變時,它將重新被計算冰肴。

我希望你喜歡閱讀這些屈藐,我確信深入研究綁定的本質(zhì)是非常有趣的熙尉。

在這個系列的下一篇博文中联逻,我們將了解不同的綁定類型。現(xiàn)在检痰,我們只研究了最基本的綁定,QQmlBinding铅歼,但我們知道還存在更多的綁定類型公壤,比如編譯后綁定厦幅。它們神秘的面紗即將被揭開,敬請關(guān)注缚态!

如果有什么疑問或者對QML應用和研究感興趣的朋友,歡迎加入我們進行討論(QQ群:280689979)玫芦。如需轉(zhuǎn)載桥帆,無須我們授權(quán)医增,但需要注明原文鏈接(該文的鏈接),及原作者老虫,謝謝叶骨!

上一篇:QML文件加載? ? ?下一篇 綁定類型

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市祈匙,隨后出現(xiàn)的幾起案子忽刽,更是在濱河造成了極大的恐慌,老刑警劉巖夺欲,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跪帝,死亡現(xiàn)場離奇詭異,居然都是意外死亡些阅,警方通過查閱死者的電腦和手機伞剑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來市埋,“玉大人黎泣,你說我怎么就攤上這事$突眩” “怎么了聘裁?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長弓千。 經(jīng)常有香客問我,道長献起,這世上最難降的妖魔是什么洋访? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮谴餐,結(jié)果婚禮上姻政,老公的妹妹穿的比我還像新娘。我一直安慰自己岂嗓,他們只是感情好汁展,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著厌殉,像睡著了一般食绿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上公罕,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天器紧,我揣著相機與錄音,去河邊找鬼楼眷。 笑死铲汪,一個胖子當著我的面吹牛熊尉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播掌腰,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼狰住,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了齿梁?” 一聲冷哼從身側(cè)響起催植,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎士飒,沒想到半個月后查邢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡酵幕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年扰藕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芳撒。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡邓深,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出笔刹,到底是詐尸還是另有隱情芥备,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布舌菜,位于F島的核電站萌壳,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏日月。R本人自食惡果不足惜袱瓮,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望爱咬。 院中可真熱鬧尺借,春花似錦、人聲如沸精拟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜂绎。三九已至栅表,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間师枣,已是汗流浹背谨读。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留坛吁,地道東北人劳殖。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓铐尚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親哆姻。 傳聞我的和親對象是個殘疾皇子宣增,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容