iOS熱重載實現(xiàn)

前言

每次從開發(fā)Flutter開發(fā)切回到原生開發(fā)時候最不習(xí)慣的就是原生沒有熱重載功能模捂。
簡單地調(diào)一下字體顏色捶朵,view大小都要重新編譯,既耗時又費力狂男。

所以想了一下可不可以讓原生開發(fā)也可以享受到熱重載功能综看,在UI調(diào)試下可以做到 "即寫即看"。

iOS端的熱更新主要可以分成倆大塊岖食。

一種是基于JSCore红碑。
它建立起了Objective-C 與 JavaScript通的橋梁。
代表框架有,React Native析珊, Weex羡鸥,JSPatch? 等等優(yōu)秀框架。

還有一種是更為小眾一些的忠寻。
自己實現(xiàn)了一個 OC 語法的簡單解釋器惧浴,包含了基礎(chǔ)的詞法分析與語法分析,從而能夠在運行期將 OC 代碼生成抽象語法樹 AST 然后進行執(zhí)行奕剃。再通過 runtime 進行方法替換 方法添加等操作衷旅,進而實現(xiàn)了動態(tài)化的效果。
代表框架有纵朋,OCEval, OCRunner 等框架 (據(jù)我觀察貌似都是獨立開發(fā)者柿顶,目前暫時還無法做到商用級別。但也可以拿他們的基礎(chǔ)框架進行魔改操软,大部分基礎(chǔ)工作已經(jīng)做完了)嘁锯。

React Native 與 Weex 比較重,需要項目級支持聂薪。
JSPatch 是需要下發(fā)js代碼進行熱修復(fù)家乘,雖然可以滿足需求,但弊端也比較明顯胆建,那就是需要將OC代碼強行翻譯成JS語法烤低,大段落使用時候不是很方便。
用OC語法解釋器笆载,雖然可以做到OC代碼不轉(zhuǎn)譯, 但還是需要對框架做一些魔改才可以按照自己習(xí)慣方便使用? (自己做語法分析, 生成抽象語法樹 這塊還是值得研究的扑馁。理論上可以做到 下發(fā)OC代碼, 可以基于 解釋器+runtime 做到整個app都動態(tài)化)。

我設(shè)想的場景是只是在debug環(huán)境下凉驻,即寫即看腻要。
線上修復(fù)不想打擦邊球, 以及引入一些不可靠的因素。
所以才去的方案是通過動態(tài)庫來在開發(fā)階段做到動態(tài)化涝登。

眾所周知Objective-C 這門語言天生具有動態(tài)性雄家,可以任意的在運行時替換方法, 成員變量等等。那這樣就聯(lián)想到可不可以下發(fā)動態(tài)庫胀滚,在運行時加載這個動態(tài)庫并替換新加載進來的動態(tài)庫里類對象/元類對象的一些信息趟济。這樣就可以再開發(fā)環(huán)境下做到 "即寫即看"的效果。


熱重載demo


實踐

流程圖

項目搭建

項目分倆部分咽笼。

第一部分:? macOS項目(監(jiān)聽熱重載項目文件變化)

第二部分:? iOS項目(熱重載目標項目)

第一部分 (監(jiān)聽文件變化項目)

首先我們需要有一個mac端程序監(jiān)聽指定文件夾下文件的變化顷编,從而將保存變化后的.m文件通過運行預(yù)先編寫好的 shell腳本進行 編譯 成 .o 文件并打包成 一個dylib,發(fā)送給app剑刑。

shell腳本整體流程思路 :
1) 接收 要編譯的 .m 文件路徑 以及 .m引用的其他類的 .o文件路徑 (這一步是生成動態(tài)庫必要的媳纬,不然會因為LinkFileList 引用出錯而無法編譯出一個dylib)
2) 通過clang 先將 .m 編譯成 .o 并儲存起來
3) 再將重新編譯后的 .o 文件編譯成dylib
4) 這時候可以多加一個參數(shù)判斷是向真機還是模擬器發(fā)送動態(tài)庫双肤。如果向真機發(fā)送的話需要做個簽名操作,不然真機無法dlopen 這個 dylib
5) 向目標傳輸編譯好的dylib

編譯腳本

監(jiān)聽軟件部分

這部分可以通過?FSEventStream 來實現(xiàn)監(jiān)聽文件變化钮惠。
shell腳本方面可以使用?NSTask 來執(zhí)行腳本名茅糜。
* 我是在這里生成了LinkFileList。是從變化文件字符串中提取出 引入文件并遍歷拼接成一個有效 依賴鏈接素挽。不知道有沒有更好的方法生成該文件需要依賴的 LinkFileList蔑赘。

最終成型后的mac端監(jiān)聽軟件

mac端監(jiān)聽程序

這里勾選中真機 并 鍵入手機ip地址將會熱重載手機端app。解除勾選將會熱重載模擬器毁菱。這樣的話倆者都可以兼顧了米死。

第一個部分這樣就差不多可以了。

第二部分 (熱重載目標項目)

首先項目里面要搭建一個http服務(wù)贮庞,我們這里選擇的是用 GCDWebServer。

GCDWebServer 是一個基于GCD 可以用于macOS & iOS 上的一個輕量的HTTP server究西,該庫實現(xiàn)了基于web的文件上傳等功能窗慎。

然后要開始編寫解析mac上編譯并連接好的dylib了。

① 通過 dlopen 打開傳進來的 dylib
? ? dlopen(dylibPatch,RTLD_NOW)
② 獲取內(nèi)存中所有鏡像
? ? int32_t images= _dyld_image_count();? ? // 所有內(nèi)存中鏡像
③ 循環(huán)鏡像獲取剛剛注入的動態(tài)庫鏡像卤材。(這個步驟是必須的遮斥,不然會踩坑)
? ? ? ? for(uint32_ti =0; i < images; i++) {
? ? ? ? ? ? pszModName =_dyld_get_image_name(i);
? ? ? ? ? ? if(!strcmp(pszModName, dylibPatch)){? // 判斷鏡像地址是否與傳進來的dylib地址一致? ? ? ? ? ?
? ? ? ? ? ? ? ? base? = (void*)_dyld_get_image_header(i);
? ? ? ? ? ? ? ? slide =_dyld_get_image_vmaddr_slide(i);
? ? ? ? ? ? }
? ? ? ? }
④ 獲取注入動態(tài)庫結(jié)構(gòu)體地址
? ? ? ? Dl_infoinfo;
? ? ? ? dladdr((mach_header_t*)base, &info);
? ? ? ? machHeader1 = (structmach_header_64*)info.dli_fbase;
⑤ mach-O 文件里面的 class列表信息存在Data斷。獲取data段 classList 信息 (這個節(jié)列出了所有的class扇丛,包括元類對象)
? ? ? ? uint64_tsize =0;
? ? ? ? char*referencesSection =getsectdatafromheader_64(machHeader1,? ? ? ? ? ? "__DATA","__objc_classlist", &size );

用爛蘋果解析dylib后的DATA段 Class_list信息

⑥ 獲取注入dylib 類對象
? ? Class class = classReferences[i];
⑦ 對象替換
? ? // 獲取要替換類名稱
? ? constchar*className = class_getName(newClass);
? ? // 獲取當前內(nèi)存中類對象
? ? Class oldClass = objc_getClass(className);
? ? // 判斷是否是注入進來的類對象
? ? if? ( newClass != oldClass ) {
? ? ? ? 開始進行方法替換
? ? }
⑧ 刪除掉傳上來的動態(tài)庫
? ? [[NSFileManager defaultManager] removeItemAtPath:patch error:nil];
⑨ 發(fā)送廣播
? ? dispatch_async(dispatch_get_main_queue(), ^{
? ? ? ? ? ? [[NSNotificationCenter defaultCenter] postNotificationName:@"DWHotReload" object:nil];
? ? });
? ?* 這一步可以優(yōu)化成通過消息轉(zhuǎn)發(fā)來調(diào)用术吗。demo圖省事直接發(fā)了個廣播

待解決問題

上述步驟就是個大概的一個解析流程。
如果想要做到真正項目應(yīng)用級的話帆精,需要潤色點是shell腳本, 引用三方庫時候鏈接編譯問題较屿。
支持swift。

期待最后的落地場景

最終期望落地是卓练,測試的同學(xué)在debug頁面開啟接收dylib開關(guān)隘蝎,開發(fā)同學(xué)只要本地修復(fù)問題后直接下發(fā)dylib,直接在測試同學(xué)設(shè)備上修正好襟企。 因為是直接編寫的OC/swift 代碼所以嘱么,不會像是使用jspatch 需要最終回歸一下正式代碼。這條路很漫長顽悼。曼振。。 慢慢走蔚龙。

推薦框架

強烈推薦injection的框架冰评。oc項目無侵入的可以直接熱重載。但swift 項目在有些bridge時候會發(fā)生異常府蛇。

誤區(qū)

網(wǎng)上很多文章都再說dlopen只能在模擬器上使用集索,其實并不正確的。
真機上無法dlopen加載dylib,大體是犯了倆個錯誤务荆。
一妆距,編譯時target依賴的是x86架構(gòu)
二,打包成dylib后沒有做有效簽名
如果這倆點都做了其實dylib可以在真機上dlopen加載成功 (逆向開發(fā)后的插件就是dylib函匕,它能注入到真機二進制文件里咱們自己的也必然可以)

* 本demo也參考了部分injection思路娱据,并使用了該工程里的方法互換方法。

demo地址

https://github.com/378804441/DWHotReload
demo? 分為三個部分
① 本地監(jiān)聽文件改變工程 (FSEventStreamDemo)
② 編譯shell腳本 (shell)
③ Demo工程 (DWDebugHR)
注意: 工程直接跑不起來盅惜,要想跑起來的話可以按照錯誤提示自己配置下路徑中剩。開發(fā)階段我都寫得我本地絕對路徑 哈哈哈哈

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市抒寂,隨后出現(xiàn)的幾起案子结啼,更是在濱河造成了極大的恐慌,老刑警劉巖屈芜,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件郊愧,死亡現(xiàn)場離奇詭異,居然都是意外死亡井佑,警方通過查閱死者的電腦和手機属铁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來躬翁,“玉大人焦蘑,你說我怎么就攤上這事『蟹ⅲ” “怎么了例嘱?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長迹辐。 經(jīng)常有香客問我蝶防,道長,這世上最難降的妖魔是什么明吩? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任间学,我火速辦了婚禮,結(jié)果婚禮上印荔,老公的妹妹穿的比我還像新娘低葫。我一直安慰自己,他們只是感情好仍律,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布嘿悬。 她就那樣靜靜地躺著,像睡著了一般水泉。 火紅的嫁衣襯著肌膚如雪善涨。 梳的紋絲不亂的頭發(fā)上窒盐,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天,我揣著相機與錄音钢拧,去河邊找鬼蟹漓。 笑死,一個胖子當著我的面吹牛源内,可吹牛的內(nèi)容都是我干的葡粒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼膜钓,長吁一口氣:“原來是場噩夢啊……” “哼嗽交!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起颂斜,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤夫壁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后沃疮,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掌唾,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年忿磅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凭语。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡葱她,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出似扔,到底是詐尸還是另有隱情吨些,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布炒辉,位于F島的核電站豪墅,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏黔寇。R本人自食惡果不足惜偶器,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望缝裤。 院中可真熱鬧屏轰,春花似錦、人聲如沸憋飞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽榛做。三九已至唁盏,卻和暖如春内狸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背厘擂。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工昆淡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人驴党。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓瘪撇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親港庄。 傳聞我的和親對象是個殘疾皇子倔既,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容