深入解析QML引擎, 第1部分:QML文件加载
译者注:这个解析QML引擎的文章共4篇,分析非常透彻,在国内几乎没有找到类似的分析,为了便于国内的QT/QML爱好者和工作者也能更好的学习和理解QML引擎,故将这个系列的4篇文章翻译过来。翻译并不是完全直译,有不足之处,请指正,谢谢!
———————————————————————————————————————————
在这个系列的博文中,我们将深入探寻隐藏在QML引擎背后的那些不为人知的玄机,一步步揭晓它内部实现的原理。这些博文都是基于Qt5版本的QtQuick,QtQuick 2.0来深入分析的。
众所周知,QML文件中每个元素都对应于一个C++类。QML引擎在加载QML文件时,会为文件中的所有元素以某种方式创建相应的C++对象。在这篇博文中,我们将探寻QML引擎从解析QML文件开始,到形成一棵完整的C++对象树的整个过程。Qt官方文档已经大篇幅地阐述了QML和C++是如何协同工作的,这些内容值得你花上一些时间评鉴一番。在这个系列的博文中,我假设你已经对该官方文档有所了解。
例子
首先我们将使用一个不怎么有用,也不怎么令人感到兴奋,但却能体现QML有趣的地方的例子:

上面这个QML文件包含三个元素:Rectangle(矩形)、Text(文本)和MouseArea(鼠标区域)。这些元素分别对应于C++类:QQuickRectangle、 QQuickText和QQuickMouseArea。这些类只被导出到QML中,在C++版本中它们是私有的,不能被Qt用户使用。这些元素将被绘制在一个OpenGL scenegraph中,绘制及事件处理都是由QQuickView控制的。我们可以利用KDAB的Qt自检工具GammaRay来验证QML文件对应的C++对象树:

和我们预想的一样,QQuickMouseArea和QQuickText类显示在对象树中。但QQuickRectangle_QML_0又是什么呢?在Qt的源代码中压根没有同名的C++类! 这个问题我们会在后续的博文中解答,你可以暂时假设它就是QQuickRectangle类型的一个对象。
让我们更进一步,用QML分析器(QML profiler)来运行并分析这个例子程序:

如上图所示,在场景设置过程中,执行了少许的绘制,这和我们预想的一样。后续的创建阶段花费了大量的时间。但还有一个编译阶段,编译阶段是个啥啊? 都干些什么事?是在创建机器码吗?看来是时候更深入一点地分析加载QML文件的代码了。
QML文件加载步骤
当加载QML文件时,会执行三个不同的步骤,接下来我们将深入研究这些步骤:
1.解析
2.编译
3.创建
解析
首先,QML文件是由QQmlScript::Parser这个解析器来解析的。该解析器内部的绝大多数内容都是由��语法文件自动生成的。我们这个例子的抽象语法树(AST)看起来是这样的:

这个AST是比较底层的东西,紧接着,它将被转换成更高层级结构的对象,属性和值。这是通过使用一个访问器遍历AST来完成的。这一步的对象就和QML中的元素一一对应上了,且对象的属性/值和QML元素的属性/值也一一对应上。我们的例子中Rectangle元素的属性“color”,其对应的值是“lightsteelblue”,它们就是属性/值的关系。即使像onClicked这样的信号处理程序也被看作只是属性/值的关系,属性是onClicked,值就是JavaScript函数体。
编译
在理论上,对象,属性和值已经足够用于创建对应的C++对象,并给属性赋上对应的值。但这些对象,属性和值依然过于原始,在创建C++对象之前,还需要进行一些后置处理。这些后置处理是由QQmlCompiler来完成的,这对应于QML分析器(QML profiler)输出中看到的编译阶段。该编译器会为QML文件创建了一个QQmlCompiledData对象。 用QQmlCompiledData创建C++对象比直接使用对象、属性和值来创建C++对象快了很多。当多次使用同一个QML文件,该文件也只会编译一次。比如在一个工程中,其他所有的QML文件都会用到的Button.qml,编译时Button.qml只会被编译一次。Button.qml的QQmlCompiledData会一直保存,每次使用该按钮组件时,都会根据这个Button.qml的QQmlCompiledData来创建C++对象。在编译之后,就是创建阶段,这在QML分析器(QML profiler)的输出中可以看到。
综上所述:解析和编译QML文件都只会做一次,在此之后,都是直接使用QQmlCompiledData对象来快速创建C++对象。
创建
我不会深入研究QQmlCompiledData的细节,但有一个东西可能会引起你的注意:“QByteArray bytecode”成员变量。实际上,创建C++对象并给它的属性赋值的指令会被编译为了字节码,之后由字节码解析器解析!字节码包含了一堆指令,当这些指令执行时,QQmlCompiledData的其余部分仅是辅助数据。
在创建阶段,字节码是由QQmlVME类解析的。阅读QQmlVME::run()这个函数的代码,里面有一个循环用于遍历字节码包含的所有指令,在循环体内部,有一个很大的判定不同指令类型的switch语句。运行带有QML_COMPILER_DUMP=1的例子程序,我们可以看到字节码所包含的每个指令:

CREATE_SIMPLE 指令是最重要的,它会创建一个C++对象,然后注册到QQmlMetaType中的一个用于注册对象的数据库。
STORE_INTEGER 指令为属性赋一个整数类型值。
STORE_SIGNAL 指令用于创建信号的处理器。
STORE_ * _BINDING 指令用于创建一个属性的绑定。更多关于绑定的内容会在这个系列的下一篇博文中说明。
SETID 指令设置一个对象的标识(id),它不是一个普通的属性。
VME有一个对象栈,STORE_*的所有指令都操作栈顶对象,FETCH指令在堆顶放置一个特定对象,POP指令会移除顶部对象。所有指令都大量使用整数索引,例如STORE_COLOR指令写入属性41,41就是目标对象的元对象的属性索引。
综上所述:一旦一个QML文件编译完成,创建它的实例就只和编译后的字节码的执行有关。
结论
在这篇博文的最后,我们已经揭示了一个QML文件是如何进行解析、处理、编译的,以及VME是如何创建对象的。我希望你已经更加深入地理解了QML引擎。
下一篇的博文将进一步探讨属性绑定是如何进行的,敬请关注!
作者:猿基地
链接:https://www.jianshu.com/p/3e959cbaff3a
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
深入解析QML引擎, 第1部分:QML文件加载的更多相关文章
- QML从文件加载组件简单示例
QML从文件加载组件简单示例 文件目录列表: Project1.pro QT += quick CONFIG += c++ CONFIG += declarative_debug CONFIG += ...
- iOS开发 XML解析和下拉刷新,上拉加载更多
iOS开发 XML解析和下拉刷新,上拉加载更多 1.XML格式 <?xml version="1.0" encoding="utf-8" ?> 表示 ...
- html文件在head标签中引入js地址和直接写js代码,所用时间是不同的,因为引入js地址,文件加载的时候需要通过通讯协议去解析地址,读取外部文件
html文件在head标签中引入js地址和直接写js代码,所用时间是不同的,因为引入js地址,文件加载的时候需要通过通讯协议去解析地址,读取外部文件
- 深度解析qml引擎---(1)Qml文件加载
"美的事物是永恒的喜悦" --- 济慈 ...
- 《Drools7.0.0.Final规则引擎教程》Springboot+规则重新加载
在<Drools7.0.0.Final规则引擎教程>之Springboot集成中介绍了怎样将Drools与Springboot进行集成,本篇博客介绍一下集成之后,如何实现从数据库读取规则并 ...
- module_init宏解析 linux驱动的入口函数module_init的加载和释放
linux驱动的入口函数module_init的加载和释放 http://blog.csdn.net/zhandoushi1982/article/details/4927579 void free_ ...
- Universal-Image-Loader解析(三)——用ListView和ViewPager加载网络中的图片
现在我们终于可以通过这个框架来实现ListView中加载图片了,至于ViewPager还是别的,原理其实都是一样的 一.ListView 1.布局文件 list_layout.xml & ...
- 深入解析QML引擎, 第2部分: 绑定(Bindings)
原文 QML Engine Internals, Part 2: Bindings 译者注:这个解析QML引擎的文章共4篇,分析非常透彻,在国内几乎没有找到类似的分析,为了便于国内的QT/QML爱好 ...
- Away3D引擎学习笔记(一)资源加载解析块
前文:Away3D断断续续用了一段时间了,三维相关的很多算法,计算转换还是有点绕,整理些自己觉得还有点意思东西,希望大家有用. 三维开始,Away3D构架你场景那几行代码各处都有,这里就不copy了, ...
随机推荐
- Kali-linux密码在线破解
为了使用户能成功登录到目标系统,所以需要获取一个正确的密码.在Kali中,在线破解密码的工具很多,其中最常用的两款分别是Hydra和Medusa.本节将介绍使用Hydra和Medusa工具实现密码在线 ...
- jquery mobile changepage的三种传参方法介绍
本来觉得changePage 那么多option,传几个参数应该没问题结果翻遍国内外网站,基本方法只有三种 1,显性传参,就是利用url这个地址把参数带上,然后到changepage后的新页面,用函数 ...
- Maven下使用Junit对Spring进行单元测试
主要步骤 1. 在工程的pom文件中增加spring-test的依赖: <dependency> <groupId>org.springframework</groupI ...
- PAT——1074. 宇宙无敌加法器(20)
地球人习惯使用十进制数,并且默认一个数字的每一位都是十进制的.而在PAT星人开挂的世界里,每个数字的每一位都是不同进制的,这种神奇的数字称为“PAT数”.每个PAT星人都必须熟记各位数字的进制表,例如 ...
- MySQL 日常运维业务账号权限的控制
在MySQL数据库日常运维中,对业务子账号的权限的统一控制十分必要. 业务上基本分为读账号和写账号两种账号,所以可以整理为固定的存储过程,让数据库自动生成对应的库的账号,随机密码.以及统一的读权限,写 ...
- 如何使用gitbash 把你的代码托管到github
1.如果你没有创建仓库 mkdir vuex-playList cd vuex-playList git init touch README.md git add README.md git comm ...
- css动画Demo---水波动画和边框动画
先上效果图: 水波动画: 边框动画: 1.水波动画 实现代码 <!DOCTYPE html> <html lang="en"> <head> & ...
- C#串口通信及数据表格存储
1.开发环境 系统:win10 开发工具:Visual Studio 2017 2.界面设计 串口通信的界面大致如此,在此基础上添加项目所需的调试指令与数据存储功能,界面排布方面可参考其他教程. 3. ...
- Java ConcurrentHashMap 源代码分析
Java ConcurrentHashMap jdk1.8 之前用到过这个,但是一直不清楚原理,今天抽空看了一下代码 但是由于我一直在使用java8,试了半天,暂时还没复现过put死循环的bug 查了 ...
- 用GO写一个后台权限管理系统
最近用GO写了一个后台权限管理系统,在WIN10和ubuntu下部署,在win系统下编译ububtu的部署文件要先做如下配置 set GOARCH=amd64 set GOOS=linux go bu ...