QQ邮箱是一个除了收邮件和发邮件的基本功能之外,具有其它一些小的办公功能的邮箱客户端。其中记事本是办公功能之一。
但是集成了记事本等小功能的邮箱,体积较大。为控制客户端体积,需要将非基本功能的一些功能模块脚本化,不编译到App当中,达到控制App体积,修复和升级方便的目的。
本文包括3个部分:
- 原理简介
- 优化经验
- 教训
原理简介
Lua作为一门脚本语言,借助Lua脚本引擎和Lua运行库,来解释执行Lua脚本。我们选择Wax作为Lua引擎,来执行Lua。它利用Objective-c Runtime的特性,在Lua脚本中调用OC的类和方法。Wax由probablycorey创建,目前由阿里团队负责维护。
Wax引擎原理在于:
Wax包含一个Lua的解释器和运行库,从而能解释执行标准Lua脚本;
Wax扩充了Lua的语法,为Lua调用OC类及OC类动态添加方法提供了基础。
使用Lua实现的优势(参考此链接):
- 自动垃圾收集 无需手动创建对象,即无需用alloc方法进行对象的初始化工作;
- 更少的代码 无需头文件,不用static类型、数组和逐字化声明字典;
使用Lua能调用每一个Cocoa,UITouch,Foundation等类和框架;这种调用OC类的能力也为功能模块使用Lua脚本实现提供了基础。 - Lua使用closure来替代OC中的Blocks
- 内置正则匹配库
- 超简单的HTTP 请求
- 动态下发和执行
Lua代码可以作为资源文件下发到app端,然后在app端进行动态执行。从而能够减少app安装包的体积,并能动态发布和维护代码。邮箱的记事本模块目前采用此模式,后面会详解。
优化经验
1. 多线程和锁
分析Lua引擎和Wax框架,锁会在下述情况下产生:
a. Lua底层api对运行上下文lua_State加的锁
b. Wax框架在C和Lua数据交换时,为保证线程安全所加的锁
解决方案:
使用Lua协程,或者用Lua协程配合iOS的GCD框架,使得父协程和子协程在不同的空间中运行,从而支持竞争式多协程。
Lua框架本身并不支持多线程,但OC中很多情况下得使用多线程,所以在OC转换成Lua的过程中,需要让Lua支持多线程才能做到和原生OC写的效果一致。解决方案是利用协程,配合GCD达到多线程的效果。