原文 QML Engine Internals, Part 3: Binding Types

译者注:这个解析QML引擎的文章共4篇,分析非常透彻,在国内几乎没有找到类似的分析,为了便于国内的QT/QML爱好者和工作者也能更好的学习和理解QML引擎,故将这个系列的4篇文章翻译过来。翻译并不是完全直译,有不足之处,请指正,谢谢!

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

上一篇 绑定(Bindings)   下一篇 自定义解析器

这篇博文是深入解析QML引擎系列博文的第三篇。在上一篇博文中,我们揭示了QML引擎中的绑定是如何运作的。在这篇文章中,我们将深入了解不同的绑定类型。某些内容是我在开发者日对话QtQuick Under the Hood中讲过的。除此之外,这篇博文中将涵盖一些新的内容。

简要回顾

在回顾之前,让我们快速地浏览一个简单的绑定:

 
图1 绑定例子

每一个像这样的绑定实际上是一个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绑定。

 
图2 V4和V8绑定执行效率对比

如你所见,在这种特定情形下,V4字节码引擎的确比V8快多了。

从本质上说,V4就是一个寄存器机器。和CPU相同的是,它具有的寄存器,用来存储临时值。不同的是,它不会从内存加载和储存值——它从类的属性加载和储存值。设置环境变量QML_BINDINGS_DUMP=1,让我们看一个简单的绑定:

 
图3 简单绑定例子

其指令输出是:

 
图4 绑定执行指令

如你所见,属性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组件实例化过程中一个一个地进行编译。

这有一个展示所有绑定类型的(没啥用的)例子:

 
图5 三种类型的绑定的例子

设置环境变量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)。如需转载,无须我们授权,但需要注明原文链接(该文的链接),及原作者,谢谢!

上一篇 绑定(Bindings)  下一篇 自定义解析器

作者:猿基地
链接:https://www.jianshu.com/p/d752ba649817
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

深入解析QML引擎, 第3部分: 绑定类型的更多相关文章

  1. 深入解析QML引擎, 第2部分: 绑定(Bindings)

    原文  QML Engine Internals, Part 2: Bindings 译者注:这个解析QML引擎的文章共4篇,分析非常透彻,在国内几乎没有找到类似的分析,为了便于国内的QT/QML爱好 ...

  2. 深度解析qml引擎---(2)绑定(binding)

    强烈的希望是人生中比任何欢乐更大的兴奋剂.--尼采 上一篇文章讲了QML引擎加载qml文件的过程,大体过程是,解析qml文件,然后为文件中的每个元素创建对应的c++对象.例如,qml文件中如果使用了T ...

  3. 深入解析QML引擎, 第1部分:QML文件加载

    译者注:这个解析QML引擎的文章共4篇,分析非常透彻,在国内几乎没有找到类似的分析,为了便于国内的QT/QML爱好者和工作者也能更好的学习和理解QML引擎,故将这个系列的4篇文章翻译过来.翻译并不是完 ...

  4. 深入解析QML引擎, 第4部分: 自定义解析器

    原文 QML Engine Internals, Part 4: Custom Parsers ——————————————————————————————————————————— 上一篇 绑定类型 ...

  5. 深度解析qml引擎---(1)Qml文件加载

                                                                        "美的事物是永恒的喜悦" --- 济慈    ...

  6. QML引擎的演进,第一部分

    原文链接:Lars Knoll – Evolution of the QML engine, part 1 QML作为一项技术对于Qt的成功变得越来越重要.它允许创建流畅的动画界面,与现今的市场预期相 ...

  7. WCF绑定类型选择

    WCF绑定类型选择   发布日期:2010年12月10日星期五 作者:EricHu   在开发WCF程序时,如何选择一个适合的绑定对于消息传输的可靠性,传输模式是否跨进程.主机.网络,传输模式的支持. ...

  8. MyBatis源码解析(十二)——binding绑定模块之MapperRegisty

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6758456.html 1.回顾 之前解析了解析模块parsing,其实所谓的解析模块就是为 ...

  9. 腾讯QQ团队开源分布式后台毫秒服务引擎全解析:引擎架构、RPC、灰度……

    腾讯QQ团队将于12月4日开源一个服务开发运营框架,叫做毫秒服务引擎(Mass Service Engine in Cluster,MSEC),它集RPC.名字发现服务.负载均衡.业务监控.灰度发布. ...

随机推荐

  1. Django中模型(二)

    Django中模型(二) 三.定义模型 1.模型.属性.表.字段间的关系: 一个模型类在数据库中对应一张表:在模型类中定义的属性,对应该模型对照表中的字段. 2.定义属性 A.概述 ·django根据 ...

  2. 【LeetCode每天一题】Remove Duplicates from Sorted Array II(移除有序数组中重复的两次以上的数字)

    Given a sorted array nums, remove the duplicates in-place such that duplicates appeared at most twic ...

  3. ethereumjs/ethereumjs-blockchain-1-简介和API

    https://github.com/ethereumjs/ethereumjs-blockchain SYNOPSIS概要 A module to store and interact with b ...

  4. pymongo的安装和使用

    1.安装 MongoDB的python接口pymongo的安装方法有多种,如源码.easy_install.pip都可以.采用pip安装,很简单. pip install pymongo 安装完成后可 ...

  5. Shell笔记-04

    如果表达式中包含特殊字符,Shell 将会进行替换.例如,在双引号中使用变量就是一种替换,转义字符也是一种替换. 举个例子: #!/bin/bash a=10 echo -e "Value ...

  6. HDU 1059(多重背包加二进制优化)

    http://acm.hdu.edu.cn/showproblem.php?pid=1059 Dividing Time Limit: 2000/1000 MS (Java/Others)    Me ...

  7. 到底什么是dp思想(内含大量经典例题,附带详细解析)

    期末了,通过写博客的方式复习一下dp,把自己理解的dp思想通过样例全部说出来 说说我所理解的dp思想 dp一般用于解决多阶段决策问题,即每个阶段都要做一个决策,全部的决策是一个决策序列,要你求一个 最 ...

  8. 数据库中间件mycat安装与使用

    1.下载 # wget http://dl.mycat.io/1.6-RELEASE/Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz 2.安装 ...

  9. 安卓apk重新签名教程

    可能大家会有疑问,为什么安卓apk文件要重新签名,签名后有什么作用.这里我简单说一下,如果大家一直都是用官方的app的话那是不需要重新签名的.重新签名是对官方app进行了修改(如icon.图片.代码等 ...

  10. oracle json 解析函数

    CREATE OR REPLACE TYPE ty_tbl_str_split IS TABLE OF ty_row_str_split;CREATE OR REPLACE TYPE ty_row_s ...