iOS熱修復(fù)方案可行性研究以及Aspects修復(fù)方案的實(shí)踐

前言:

伴隨著企業(yè)的快速發(fā)展弯囊,承載著移動(dòng)互聯(lián)網(wǎng)業(yè)務(wù)的App的更新迭代要求越來越高。尤其是在中國誊爹,App的迭代速度很快芬首,有時(shí)App需要緊急發(fā)版來處理線上業(yè)務(wù)和技術(shù)等問題。由于Apple審核制度的限制吮成,在Apple提速了審核速度的情況下橱乱,大多數(shù)App審核往往需要1到2天。但是這仍舊無法滿足App快速更新的需求粱甫。所以泳叠,想不通過發(fā)版,解決線上問題的方案一直為iOS端技術(shù)研發(fā)人員所青睞茶宵。

但是隨著公開版本的JSPatch被Apple拒絕危纫。熱更新這個(gè)概念似乎冷了下來。但是App更新與審核速度的矛盾從沒有解決。分析种蝶、構(gòu)思契耿,探索和找尋解決這個(gè)問題的合理方案。

理論可行性分析:

查閱了幾種熱更新的方案螃征,JSPatch搪桂、Aspects、Stringer盯滚、TTPatch踢械。分別對其實(shí)現(xiàn)原理、上架審核現(xiàn)狀以及文檔和資料完備性等方面進(jìn)行分析魄藕。

拒絕JSPatch:

關(guān)于Apple拒絕JSPath事件的分析内列。

Apple對于App的開發(fā)傾向于Native。但是拒絕JSPatch并不是因?yàn)槠涫褂昧薐S動(dòng)態(tài)下發(fā)代碼泼疑,而是因?yàn)檫@種方式存在著安全漏洞德绿。可能被第三方篡改造成隱患退渗。正式因?yàn)檫@樣移稳,所以在市面上滴滴出行App仍舊存在熱更新。從一些資料上看到会油,滴滴的方案是從技術(shù)層面上對于下發(fā)的JS代碼做了安全性校驗(yàn)个粱。使得滴滴的App能夠識別這些JS是由滴滴下發(fā)的。從而避免了Apple拒絕JSPatch熱更新的安全漏洞翻翩。所以都许,重要的是安全性。而不是熱更新的手段嫂冻。

關(guān)于Aspects方案:

Aspects的原理與消息轉(zhuǎn)發(fā)機(jī)制密切相關(guān)胶征。Aspects主要是利用了forwardInvocation進(jìn)行轉(zhuǎn)發(fā),Aspects其實(shí)利用和kvo類似的原理桨仿,通過動(dòng)態(tài)創(chuàng)建子類的方式睛低,把對應(yīng)的對象isa指針指向創(chuàng)建的子類,然后把子類的forwardInvocation的IMP替成__ASPECTS_ARE_BEING_CALLED__服傍,假設(shè)要hook的方法名XX,在子類中添加一個(gè)Aspects_XX的方法钱雷,然后將Aspects_XX的IMP指向原來的XX方法的IMP,這樣方便后面調(diào)用原始的方法吹零,再把要hook的方法XX的IMP指向_objc_msgForward罩抗,這樣就進(jìn)入了消息轉(zhuǎn)發(fā)流程,而forwardInvocation的IMP被替換成了__ASPECTS_ARE_BEING_CALLED__灿椅,這樣就會進(jìn)入__ASPECTS_ARE_BEING_CALLED__進(jìn)行攔截處理套蒂,這樣整個(gè)流程大概結(jié)束钞支。

關(guān)于Stringer方案:

雖然Stringer聲稱速度快又好,但是怎奈查看github庫操刀,當(dāng)前開發(fā)版本號是0.3.0伸辟,可以認(rèn)為是還不夠成熟。并且其公開文檔馍刮、資料極少。Demo也不完備窃蹋。這給實(shí)際應(yīng)用帶來了麻煩卡啰。

綜合分析:

于是有了滴滴的 DynamicCocoa 這種方案,繞了一個(gè)更大的道警没,從編譯階段入手匈辱,通過 clang 把 OC 代碼編譯成自己定制的 JS 格式,再動(dòng)態(tài)下發(fā)去執(zhí)行杀迹,做到原生開發(fā)亡脸,動(dòng)態(tài)運(yùn)行,主打動(dòng)態(tài)添加功能树酪,當(dāng)然順便把修 bug 也給支持了浅碾。手機(jī) QQ 內(nèi)部也有一個(gè)類似的方案,不過更進(jìn)一步续语,他們通過 clang 把 OC 代碼編譯成自己定制的字節(jié)碼動(dòng)態(tài)下發(fā)垂谢,然后開發(fā)一個(gè)虛擬機(jī)去執(zhí)行(驚呆了),同樣實(shí)現(xiàn)了原生開發(fā)疮茄,動(dòng)態(tài)運(yùn)行滥朱,都是 NB 得很的方案。只要底層處理做得足夠好力试,也是個(gè)成本低收益高的方案徙邻,不過目前都還沒開源,還沒能看到實(shí)際效果和可靠的源碼畸裳。

綜合查閱以上幾個(gè)熱更新方案缰犁,遴選出Aspects方案。原因是躯畴,此方案可以實(shí)現(xiàn)需要的代碼修復(fù)功能民鼓,而且Aspects庫與iOS庫相關(guān)。這可以作為通過審核的有力依據(jù)蓬抄。

This is stable and used in hundreds of apps since it’s part of PSPDFKit,

an iOS PDF framework that ships with apps like Dropbox or Evernote.

綜上如果你是企業(yè)賬號那么方案就是JSPatch 1.7.2版本加自己管理補(bǔ)丁,因?yàn)樗@個(gè)平臺超過1萬會收費(fèi).但是人家js轉(zhuǎn)OC的代碼轉(zhuǎn)換器都有了開發(fā)成本很低的丰嘉。

如果是需要上架的App,并且團(tuán)隊(duì)的后臺安全性沒有得到充分保證的情況下嚷缭,Aspects是穩(wěn)妥的方案饮亏。

Aspects深入分析:

理論分析--面向切面(AOP)編程:

通過預(yù)編譯和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)給程序動(dòng)態(tài)統(tǒng)一添加功能的一種技術(shù)耍贾。可以實(shí)現(xiàn)不修改原始類的實(shí)現(xiàn)無入侵式改變應(yīng)用行為路幸,相對來講荐开,實(shí)現(xiàn)簡單,易于維護(hù)简肴』翁可以在需要的某一個(gè)類或?qū)嵗刑砑右恍┪覀冏约旱膶?shí)現(xiàn),只針對某個(gè)切面進(jìn)行Hook操作砰识,這個(gè)就是面向切面的概念(AOP能扒,aspect-oriented programming),框架Aspects是 AOP 編程的框架辫狼。

AOP是采用使用運(yùn)行時(shí)hook方法實(shí)現(xiàn)的原理(iOS的消息轉(zhuǎn)發(fā)初斑、swzziling)。

Aspects可以做什么:

AOP在開發(fā)中是一個(gè)非常重要的思想膨处,我們希望將需求分離到非業(yè)務(wù)邏輯的方法中见秤,盡可能的不影響業(yè)務(wù)邏輯的代碼。主要的應(yīng)用場景大概有以下幾種:

參數(shù)校驗(yàn):網(wǎng)絡(luò)請求前的參數(shù)校驗(yàn)真椿,返回?cái)?shù)據(jù)的格式校驗(yàn)等等鹃答;

無痕埋點(diǎn):統(tǒng)一處理埋點(diǎn),降低代碼耦合度突硝;

頁面統(tǒng)計(jì):幫助統(tǒng)計(jì)頁面訪問量挣跋;

事務(wù)處理:攔截指定事件,添加觸發(fā)事件狞换;

異常處理:發(fā)生異常時(shí)使用面向切面的方式進(jìn)行處理避咆;

熱修復(fù):AOP可以讓我們在某方法執(zhí)行前后或者直接替換為另一段代碼,我們可以根據(jù)這個(gè)思路修噪,實(shí)現(xiàn)bug修復(fù).

Aspects使用注意事項(xiàng):

會有額外的系統(tǒng)開銷查库,要避免高頻調(diào)用。hook方法不能修改 @"retain", @"release", @"autorelease", @"forwardInvocation:"黄琼。hook類中的dealloc方法只能AspectPositionBefore樊销。Aspects會產(chǎn)生額外開銷,性能不高脏款,不建議高頻調(diào)用围苫。

關(guān)于封裝庫的使用經(jīng)驗(yàn):

JS替換實(shí)例方法會覆蓋原類和其分類的方法實(shí)現(xiàn)。注意有返回值和無返回值JS方法的靈活運(yùn)用撤师。注意無參數(shù)剂府、有參數(shù)、多參數(shù)JS方法的調(diào)用剃盾。JS方法同樣可以修改某一類中代理方法的實(shí)現(xiàn)腺占。與一般方法相同淤袜。JS修改方法,可以獲得上下文衰伯,參數(shù)铡羡,方法名∫饩ǎ可以根據(jù)具體需要來進(jìn)行邏輯處理烦周。

部分JS代碼:

fixInstanceMethodAfter('UBTestViewController', 'initViewLayout', function(instance, originInvocation, originArguments){

? ? //runVoidInstanceWithNoParamter(instance, 'showProgressForCommit');

? ? //runVoidInstanceWith1Paramter(instance, 'showText:','這是熱更新測試');//替換某個(gè)方法,為該類的已有方法

? ? //下面是用JS語法設(shè)置OC的實(shí)例對象

? ? var coffeeVo = runClassWithNoParamter('UBCoffeeModel', 'new');

? ? runVoidInstanceWith1Paramter(coffeeVo, 'setBrand:','luckin');

? ? runVoidInstanceWith1Paramter(coffeeVo, 'setPrice:','16');

? ? runVoidInstanceWith1Paramter(instance, 'setCoffee:',coffeeVo);

? ? //runVoidInstanceWith2Paramter(instance, 'resetMyCoffee::',coffeeVo);

? ? console.log('這是熱更新測試 替換方法驗(yàn)證##');

});

對于block可以參考如下代碼怎顾。

//參數(shù)作為方法的參數(shù)论矾,在JS中的調(diào)用(makeMyCoffeeComplete:的參數(shù)在原OC實(shí)現(xiàn)是一個(gè)block)

fixInstanceMethodReplace('UBTestViewController', 'makeMyCoffeeComplete:', function(instance, originInvocation, originArguments){

? ? if (originArguments[0]) {//取出參數(shù),這里在對應(yīng)的OC中杆勇,這個(gè)參數(shù)是block也就是function

? ? ? ? var callback = originArguments[0];

? ? ? ? callback(20);//block? 作為參數(shù),直接調(diào)用

? ? }

? ? console.log('這是熱更新測試 替換block方法驗(yàn)證##' + originArguments[0]);

});

部分對Aspects的封裝代碼(借鑒了網(wǎng)上搜到的信息):

+ (void)_runVoidInstanceWithInstance:(id)instance selector:(NSString *)selector obj1:(id)obj1 obj2:(id)obj2 {

#pragma clang diagnostic push

#pragma clang diagnostic ignored "-Warc-performSelector-leaks"

? ? [instance performSelector:NSSelectorFromString(selector) withObject:obj1 withObject:obj2];

#pragma clang diagnostic pop

}

+ (void)_runVoidInstanceWithInstance:(id)instance selector:(NSString *)selector withObjects:(NSArray *)objects {

#pragma clang diagnostic push

#pragma clang diagnostic ignored "-Warc-performSelector-leaks"

? ? [[UBFreeFixManger sharedInstance] performSelector:NSSelectorFromString(selector) target:instance? withObjects:objects];

#pragma clang diagnostic pop

}

+ (void)_runVoidInstanceWithInstance:(id)instance selector:(NSString *)selector {

#pragma clang diagnostic push

#pragma clang diagnostic ignored "-Warc-performSelector-leaks"

? ? [instance performSelector:NSSelectorFromString(selector)];

#pragma clang diagnostic pop

}

特別的饱亿,基于Aspects的熱修復(fù)蚜退,其目的是“熱修復(fù)”,也僅僅是熱修復(fù)彪笼。不建議通過這種方式隨意修改一般功能邏輯钻注。我想這也是基于Aspects的熱修復(fù)能得到Apple認(rèn)可的原因。

以上可行性分析很多資料借鑒和來源于網(wǎng)絡(luò)配猫,Aspects方案經(jīng)過筆者仔細(xì)實(shí)踐幅恋。Demo源碼可以留言索取。

參考資料:

1泵肄、TTPatch【類JSPatch】<有成功案例捆交、JS代碼下發(fā),轉(zhuǎn)成原生代碼>

《TTPatch使用》

http://www.reibang.com/p/470a9e49b4f2

2腐巢、iOS中OC轉(zhuǎn)Javascript的工具

https://blog.csdn.net/u013602835/article/details/52777461

3品追、Aspects<數(shù)百成功案例、JS代碼下發(fā)冯丙,轉(zhuǎn)成原生代碼>

http://www.reibang.com/p/2c93446d86bd

4肉瓦、《iOS AOP框架Aspects實(shí)現(xiàn)原理》

http://www.reibang.com/p/0d43db446c5b

5、《輕量級低風(fēng)險(xiǎn) iOS 熱更新方案》

https://mp.weixin.qq.com/s/2re_s3NmOvE9RXlbGQqGDA

6胃惜、《iOSHotFixDemo

https://github.com/3KK3/iOSHotFixDemo/tree/master/iOSHotFixDemo

7泞莉、《OS 2020 熱更新》

https://blog.csdn.net/u013712343/article/details/107932706

8、《《【iOS 教程】亮劍: Stinger到底能比Aspects快多少》

https://blog.csdn.net/weixin_47143210/article/details/105603880

9船殉、iOS使用Aspects做簡單熱修復(fù)原理

https://www.pianshen.com/article/9576238931/

10鲫趁、動(dòng)態(tài)交換方法實(shí)現(xiàn)

https://blog.csdn.net/WangErice/article/details/51211328

11、iOS AOP框架Aspects實(shí)現(xiàn)原理

http://www.reibang.com/p/0d43db446c5b

12利虫、Mac啟動(dòng)本地服務(wù)

http://www.reibang.com/p/90d5fa728861

13饮寞、熱更新方案

https://srxboys.github.io/2018/06/03/%E7%83%AD%E6%9B%B4%E6%96%B0%E6%96%B9%E6%A1%88/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載孝扛,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末幽崩,一起剝皮案震驚了整個(gè)濱河市苦始,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌慌申,老刑警劉巖陌选,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蹄溉,居然都是意外死亡咨油,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門柒爵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來役电,“玉大人,你說我怎么就攤上這事棉胀》ㄉ” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵唁奢,是天一觀的道長霎挟。 經(jīng)常有香客問我,道長麻掸,這世上最難降的妖魔是什么酥夭? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮脊奋,結(jié)果婚禮上熬北,老公的妹妹穿的比我還像新娘。我一直安慰自己诚隙,他們只是感情好蒜埋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著最楷,像睡著了一般整份。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上籽孙,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天烈评,我揣著相機(jī)與錄音,去河邊找鬼犯建。 笑死讲冠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的适瓦。 我是一名探鬼主播竿开,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼谱仪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了否彩?” 一聲冷哼從身側(cè)響起疯攒,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎列荔,沒想到半個(gè)月后敬尺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贴浙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年砂吞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片崎溃。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蜻直,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出袁串,到底是詐尸還是另有隱情概而,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布般婆,位于F島的核電站,受9級特大地震影響朵逝,放射性物質(zhì)發(fā)生泄漏蔚袍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一配名、第九天 我趴在偏房一處隱蔽的房頂上張望啤咽。 院中可真熱鬧,春花似錦渠脉、人聲如沸宇整。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鳞青。三九已至,卻和暖如春为朋,著一層夾襖步出監(jiān)牢的瞬間臂拓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工习寸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留胶惰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓霞溪,卻偏偏與公主長得像孵滞,于是被迫代替她去往敵國和親中捆。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354