项目地址
https://github.com/xurunkang/NO_COMPILE
前言
对于 iOS 的中大型项目来说,编译耗时的问题永远是一个痛点。缓解的方案有许多:ccache 等缓存方案 / 优化 Xcode 配置 / 加钱堆硬件。
这些方案的出发点都是基础优化编译耗时来解决的,哪有没有一个办法可以做到不编译就执行修改后的代码呢?
基于 Objective-C 的动态特性,是完全可以做到这一点的,这也是各种热修复框架的支撑原理之一。那么如果需要做到不编译就执行修改后的代码,我们可以这样做:获取本地修改后代码 -> 转 JavaScript 或 Lua -> 模拟器执行修改后的脚本。
大致思路
获取本地修改代码
这里也有许多方法,可以手动复制,也可以自动获取。这里我是选择利用 Xcode Editor Extension 来获取到你选中的修改代码的。
Objective-C 转 JavaScript
由于整个流程我是基于 JSPatch 来开发的,所以是需要转为 JS 的脚本。这里我是写了个 node.js 的脚本来实现,转换算法是利用 https://github.com/bang590/JSPatchConvertor 中的开源代码。
模拟器执行修改后的脚本
由于已经有 JSPatch 完整的框架做支撑,这里只需要利用其中的方法 -[JPEngine evaluateScriptWithPath:] 去执行修改后的脚本即可。
具体实现流程图
- 项目和本地 Node 服务器建立 Socket 连接
- 修改 test 代码
- 利用 Xcode Editor Extension 获取修改的代码
- 将修改代码发送到本地 Node 服务器
- 执行 Node 脚本将 OC 转 JS
- 执行 Node 脚本将 JS 写入到本地文件
- 利用 Socket 通知项目更新
- 项目执行 -[JPEngine evaluateScriptWithPath:] 函数注入 JS 代码
- 重新执行 test 代码
(我是从零开始的啊,为什么markdown就要转为1呢~)
这里使用 Node.js 有两个原因:Xcode Editor Extension 不支持直接执行沙盒外脚本(所以是通过 Extension 向 Node 服务器发送 Http 请求来传递修改的代码),而且和 iOS 模拟器进行进程通信比较麻烦(尝试了 CFNotification 等几种方法都不行,所以使用 Node 搭建 Socket 服务器达到进程通信)。
视频演示
http://ocnnxadky.bkt.clouddn.com/KK_NO_COMPILE_DEMO.mp4
缺陷
自动转换脚本目前不支持如下类型:
Macro / constant variable / Enum
C function calling
GCD functions
Pointer / Struct
Getting / Setting private variable