深入解析QML引擎, 第3部分: 绑定类型
原文 QML Engine Internals, Part 3: Binding Types
译者注:这个解析QML引擎的文章共4篇,分析非常透彻,在国内几乎没有找到类似的分析,为了便于国内的QT/QML爱好者和工作者也能更好的学习和理解QML引擎,故将这个系列的4篇文章翻译过来。翻译并不是完全直译,有不足之处,请指正,谢谢!
———————————————————————————————————————————
这篇博文是深入解析QML引擎系列博文的第三篇。在上一篇博文中,我们揭示了QML引擎中的绑定是如何运作的。在这篇文章中,我们将深入了解不同的绑定类型。某些内容是我在开发者日对话QtQuick Under the Hood中讲过的。除此之外,这篇博文中将涵盖一些新的内容。
简要回顾
在回顾之前,让我们快速地浏览一个简单的绑定:
每一个像这样的绑定实际上是一个JavaScript函数,运行时由V8引擎执行。执行的结果就是函数的返回值,然后将它设置给文本属性。由于V8并不知道Qt对象和属性,当遇到一个对象(如parent)或一个属性(如width)时,它就请求QML中的上下文包裹类和对象包裹类去解析它们。当一个绑定被执行时,这些包裹类会记录那些被访问了的属性,可以自动将每个属性的改变信号(例如widthChanged())连接到一个可以重新执行绑定的槽函数。
现在我们已经重新温习了一遍绑定的工作原理,让我们趁热打铁,继续分析不同的绑定方式。
绑定方式
在上一篇文章中,我指出每一个绑定都被解析成一个QQmlBinding对象的实例。这其实是一个哄骗孩子的谎言。如果每一个绑定都由QQmlBinding表示,则开销会非常大。一个典型的QML应用,即使没有上千个绑定,至少也有成百个绑定,所以需要让每一个绑定更加轻量级。此外,当加载一个QML文件时,每一个绑定都是单独编译的。因此在加载过程中会多次调用V8编译器,给系统造成不小的开销。
QV8Bindings
为了解决QQmlBinding造成的开销问题,使用了另外一个绑定类,取了一个容易混淆的名字:QV8Bindings。QV8Bindings内部用了一个数组来存放QML文件中的所有绑定,绑定用更加轻量级的QV8Bindings::Binding结构体来表示。QML的开发者过去花了很大力气去减少这种结构的内存占用,他们甚至发现指针的最后两位因为对齐的关系而没有被使用。然后丧心病狂地利用这些空间去保存标志位,最终做到一个QV8Bindings::Binding只占用了64个字节。
QV8Bindings和QQmlBinding相比,有一个大优势是,所有绑定都是在一起编译的,所以只需要调用一次V8编译器。在QQmlCompiler ::completeComponentBuild()函数中,你会发现,在编译QML文件时,所有的绑定函数会组成一个大的JavaScript程序,并存储在QQmlCompiledData(用于包含QML文件中所有类型的编译数据)。当QML文件第一次实例化时,QV8Bindings::QV8Bindings()将对绑定程序进行编译,编译后保存在QQmlCompiledData中,然后将源代码丢弃。当再次实例化相同的QML文件时,QML引擎将直接使用QQmlCompiledData中已编译的绑定程序,并不需要再编译一次它们。然而QQmlBinding却不是这样,每次实例化QML文件都需要执行一次编译。
小结:因为QV8Bindings把QML文件中所有的绑定组织在一起,所以可以花费更少的内存,并只执行一次编译。
那为什么我们不抛弃QQmlBinding?这个类为什么还依旧存在呢?某些情况下,绑定是不可共享的,例如它们使用了闭包或者使用了eval()函数。在这种情况下,每个绑定函数需要不同的上下文。因此不能和具有相同上下文的其他绑定一起编译。因此在这种特殊情况下,将会使用QQmlBinding来表示绑定。当编译一个QML文件时,是由QQmlCompiler::completeComponentBuild()来判定采用哪种绑定方式。另外,SharedBindingTester会检测绑定应该用QV8Bindings,还是QQmlBinding。SharedBindingTester就是一个JS AST的访问者。如果你查看一下代码,你会发现SharedBindingTester也会测试哪些绑定是安全的,同时在QML文件初始化时避免多次执行绑定,源代码的提交信息做了最好的描述。
为了让QML代码更加的简洁,QQmlBinding和QV8Bindings::Binding都从QQmlAbstractBinding继承。
QV4Bindings
假如你看过一些QML引擎的代码,你很可能已注意到QV4Bindings类,这个类也是QQmlAbstractBinding的子类。它是另一个绑定类型吗?和什么有关呢?与QV8Bindings相同的是,它也是QML文件中所有绑定的集合。不同的是,QV4Bindings只保存所谓优化过的绑定,也有人错误和混淆地称之为编译过的绑定。有一些绑定是可以被优化的,它们会用QV4Bindings表示,有一些绑定不能被优化,它们会用QV8Bindings来表示。
那么这个优化是什么呢?QV4Bindings并不由V8引擎执行,它会被编译成字节码,通过一个字节码解析器执行。这个字节码编译器和解析器无法处理所有的JavaScript表达式,因为不可能提前编译所有的JavaScript。
但是为什么使用字节码呢?V8引擎会编译成机器码,难道不比一个字节码解析器快吗?结果证明它真的没有字节码解析器快,V8引擎执行绑定时,需要调用QML来解析对象和属性,这个处理需要很大的开销。另外当一个函数被多次调用时,V8引擎可能会在比较繁忙的情况下重新编译一个函数,以此做更多地优化。对于QML的情况而言,所有这些处理都会造成很大的开销,因为QML通常包含很多只有一句代码的绑定。这里有一个我为开发者日准备的基准测试结果。在测试中,我只是简单的让QML引擎执行一个绑定几百次。这是一个可以让V4引擎轻松处理的简单绑定。为了和V8引擎比较,设置环境变量QML_DISABLE_OPTIMIZER=1来完全禁用V4绑定。
如你所见,在这种特定情形下,V4字节码引擎的确比V8快多了。
从本质上说,V4就是一个寄存器机器。和CPU相同的是,它具有的寄存器,用来存储临时值。不同的是,它不会从内存加载和储存值——它从类的属性加载和储存值。设置环境变量QML_BINDINGS_DUMP=1,让我们看一个简单的绑定:
其指令输出是:
如你所见,属性width和height被加载到寄存器0和1中,然后这些寄存器乘起来,把结果保存在文本属性中(文本属性在QQuickText类中的属性编号是42)。FetchAndSubscribe指令不仅加载属性,也会监听它的改变信号,从而实现绑定的自动更新。从上文的"汇编"代码中,你还可以发现另一个优势:V4编译器是在编译时解析对象和属性,并将属性的索引保存在字节码中。所以在运行时,就可以直接通过索引访问属性,不用通过属性名字进行查找。而V8引擎则需要调用QML对象和上下文包装器来解析对象和属性,这当然会产生更多的开销。但是缺点是,V4引擎无法处理动态对象,例如那些通过setContextProperty()从C++导出的对象。如果绑定中含这种动态对象,则需要使用QV8Binding。
总结
归纳起来,有3个绑定类型,都是从QQmlAbstractBinding继承:
1. QV4Bindings::Binding
2. QV8Bindings::Binding
3. QQmlBinding
QV4Bindings是最快的,因为其使用了自定义的字节码引擎。QV8Bindings和QQmlBinding都是使用V8 JS引擎执行,但QV8Bindings将所有的绑定组织在一起,一次性编译,然而QQmlBindings会在每个QML组件实例化过程中一个一个地进行编译。
这有一个展示所有绑定类型的(没啥用的)例子:
设置环境变量QML_COMPILER_DUMP=1,你会看到QML编译器使用了两次STORE_COMPILED_BINDING,一次STORE_V8_BINDING和一次STORE_BINDING。
STORE_BINDING为QQmlBinding,它用于font.pointSize,因为绑定使用了eval(),因此不可以被共享。
anchors.centerIn的绑定和文字都是V4绑定(STORE_COMPILED_BINDING指令,QV4Bindings:: Binding类)。
最后,font.wordSpacing是一个普通的QV8Bindings::Binding(STORE_V8_BINDING指令)。V4的字节码编译器和解析器应对三元运算符完全没有问题,但是求补运算尚未实现,所以QML编译器选择使用V8绑定。
在这个系列的下一篇博文中,我们将尝试自定义解析器。如果有什么疑问或者对QML应用和研究感兴趣的朋友,欢迎加入我们进行讨论(QQ群:280689979)。如需转载,无须我们授权,但需要注明原文链接(该文的链接),及原作者,谢谢!
作者:猿基地
链接:https://www.jianshu.com/p/d752ba649817
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
深入解析QML引擎, 第3部分: 绑定类型的更多相关文章
- 深入解析QML引擎, 第2部分: 绑定(Bindings)
原文 QML Engine Internals, Part 2: Bindings 译者注:这个解析QML引擎的文章共4篇,分析非常透彻,在国内几乎没有找到类似的分析,为了便于国内的QT/QML爱好 ...
- 深度解析qml引擎---(2)绑定(binding)
强烈的希望是人生中比任何欢乐更大的兴奋剂.--尼采 上一篇文章讲了QML引擎加载qml文件的过程,大体过程是,解析qml文件,然后为文件中的每个元素创建对应的c++对象.例如,qml文件中如果使用了T ...
- 深入解析QML引擎, 第1部分:QML文件加载
译者注:这个解析QML引擎的文章共4篇,分析非常透彻,在国内几乎没有找到类似的分析,为了便于国内的QT/QML爱好者和工作者也能更好的学习和理解QML引擎,故将这个系列的4篇文章翻译过来.翻译并不是完 ...
- 深入解析QML引擎, 第4部分: 自定义解析器
原文 QML Engine Internals, Part 4: Custom Parsers ——————————————————————————————————————————— 上一篇 绑定类型 ...
- 深度解析qml引擎---(1)Qml文件加载
"美的事物是永恒的喜悦" --- 济慈 ...
- QML引擎的演进,第一部分
原文链接:Lars Knoll – Evolution of the QML engine, part 1 QML作为一项技术对于Qt的成功变得越来越重要.它允许创建流畅的动画界面,与现今的市场预期相 ...
- WCF绑定类型选择
WCF绑定类型选择 发布日期:2010年12月10日星期五 作者:EricHu 在开发WCF程序时,如何选择一个适合的绑定对于消息传输的可靠性,传输模式是否跨进程.主机.网络,传输模式的支持. ...
- MyBatis源码解析(十二)——binding绑定模块之MapperRegisty
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6758456.html 1.回顾 之前解析了解析模块parsing,其实所谓的解析模块就是为 ...
- 腾讯QQ团队开源分布式后台毫秒服务引擎全解析:引擎架构、RPC、灰度……
腾讯QQ团队将于12月4日开源一个服务开发运营框架,叫做毫秒服务引擎(Mass Service Engine in Cluster,MSEC),它集RPC.名字发现服务.负载均衡.业务监控.灰度发布. ...
随机推荐
- Django中模型(二)
Django中模型(二) 三.定义模型 1.模型.属性.表.字段间的关系: 一个模型类在数据库中对应一张表:在模型类中定义的属性,对应该模型对照表中的字段. 2.定义属性 A.概述 ·django根据 ...
- 【LeetCode每天一题】Remove Duplicates from Sorted Array II(移除有序数组中重复的两次以上的数字)
Given a sorted array nums, remove the duplicates in-place such that duplicates appeared at most twic ...
- ethereumjs/ethereumjs-blockchain-1-简介和API
https://github.com/ethereumjs/ethereumjs-blockchain SYNOPSIS概要 A module to store and interact with b ...
- pymongo的安装和使用
1.安装 MongoDB的python接口pymongo的安装方法有多种,如源码.easy_install.pip都可以.采用pip安装,很简单. pip install pymongo 安装完成后可 ...
- Shell笔记-04
如果表达式中包含特殊字符,Shell 将会进行替换.例如,在双引号中使用变量就是一种替换,转义字符也是一种替换. 举个例子: #!/bin/bash a=10 echo -e "Value ...
- HDU 1059(多重背包加二进制优化)
http://acm.hdu.edu.cn/showproblem.php?pid=1059 Dividing Time Limit: 2000/1000 MS (Java/Others) Me ...
- 到底什么是dp思想(内含大量经典例题,附带详细解析)
期末了,通过写博客的方式复习一下dp,把自己理解的dp思想通过样例全部说出来 说说我所理解的dp思想 dp一般用于解决多阶段决策问题,即每个阶段都要做一个决策,全部的决策是一个决策序列,要你求一个 最 ...
- 数据库中间件mycat安装与使用
1.下载 # wget http://dl.mycat.io/1.6-RELEASE/Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz 2.安装 ...
- 安卓apk重新签名教程
可能大家会有疑问,为什么安卓apk文件要重新签名,签名后有什么作用.这里我简单说一下,如果大家一直都是用官方的app的话那是不需要重新签名的.重新签名是对官方app进行了修改(如icon.图片.代码等 ...
- oracle json 解析函数
CREATE OR REPLACE TYPE ty_tbl_str_split IS TABLE OF ty_row_str_split;CREATE OR REPLACE TYPE ty_row_s ...