項(xiàng)目地址
https://github.com/xurunkang/NO_COMPILE
前言
對(duì)于 iOS 的中大型項(xiàng)目來(lái)說(shuō),編譯耗時(shí)的問(wèn)題永遠(yuǎn)是一個(gè)痛點(diǎn)。緩解的方案有許多:ccache 等緩存方案 / 優(yōu)化 Xcode 配置 / 加錢(qián)堆硬件。
這些方案的出發(fā)點(diǎn)都是基礎(chǔ)優(yōu)化編譯耗時(shí)來(lái)解決的,哪有沒(méi)有一個(gè)辦法可以做到不編譯就執(zhí)行修改后的代碼呢?
基于 Objective-C 的動(dòng)態(tài)特性,是完全可以做到這一點(diǎn)的,這也是各種熱修復(fù)框架的支撐原理之一。那么如果需要做到不編譯就執(zhí)行修改后的代碼,我們可以這樣做:獲取本地修改后代碼 -> 轉(zhuǎn) JavaScript 或 Lua -> 模擬器執(zhí)行修改后的腳本。
大致思路
獲取本地修改代碼
這里也有許多方法,可以手動(dòng)復(fù)制,也可以自動(dòng)獲取。這里我是選擇利用 Xcode Editor Extension 來(lái)獲取到你選中的修改代碼的。
Objective-C 轉(zhuǎn) JavaScript
由于整個(gè)流程我是基于 JSPatch 來(lái)開(kāi)發(fā)的,所以是需要轉(zhuǎn)為 JS 的腳本。這里我是寫(xiě)了個(gè) node.js 的腳本來(lái)實(shí)現(xiàn),轉(zhuǎn)換算法是利用 https://github.com/bang590/JSPatchConvertor 中的開(kāi)源代碼。
模擬器執(zhí)行修改后的腳本
由于已經(jīng)有 JSPatch 完整的框架做支撐,這里只需要利用其中的方法 -[JPEngine evaluateScriptWithPath:] 去執(zhí)行修改后的腳本即可。
具體實(shí)現(xiàn)流程圖

- 項(xiàng)目和本地 Node 服務(wù)器建立 Socket 連接
- 修改 test 代碼
- 利用 Xcode Editor Extension 獲取修改的代碼
- 將修改代碼發(fā)送到本地 Node 服務(wù)器
- 執(zhí)行 Node 腳本將 OC 轉(zhuǎn) JS
- 執(zhí)行 Node 腳本將 JS 寫(xiě)入到本地文件
- 利用 Socket 通知項(xiàng)目更新
- 項(xiàng)目執(zhí)行 -[JPEngine evaluateScriptWithPath:] 函數(shù)注入 JS 代碼
- 重新執(zhí)行 test 代碼
(我是從零開(kāi)始的啊,為什么markdown就要轉(zhuǎn)為1呢~)
這里使用 Node.js 有兩個(gè)原因:Xcode Editor Extension 不支持直接執(zhí)行沙盒外腳本(所以是通過(guò) Extension 向 Node 服務(wù)器發(fā)送 Http 請(qǐng)求來(lái)傳遞修改的代碼),而且和 iOS 模擬器進(jìn)行進(jìn)程通信比較麻煩(嘗試了 CFNotification 等幾種方法都不行,所以使用 Node 搭建 Socket 服務(wù)器達(dá)到進(jìn)程通信)。
視頻演示
http://ocnnxadky.bkt.clouddn.com/KK_NO_COMPILE_DEMO.mp4
缺陷
自動(dòng)轉(zhuǎn)換腳本目前不支持如下類(lèi)型:
Macro / constant variable / Enum
C function calling
GCD functions
Pointer / Struct
Getting / Setting private variable