關於iOS熱修復(HotPatch)技術的使用總結


蘋果做了非常多的努力來建造和維持一個健康並且干凈的應用環境。其中對現在的現狀起到很大作用的部分就是蘋果APP STORE,它是被一個十分周密的對所有提交的應用進行檢查的審批程序所保護的。盡管這個程序是被設計為用來保護IOS用戶並且確保應用程序符合蘋果的安全性和完整性的要求,體驗過這個流程的開發者可能會覺得它太復雜了並且花費了大量的時間。發布一個新的release版本或者發布一個已經存在的APP的補丁也要遵守這個流程,這對於一個想要給一個影響現有APP用戶的重要bug或者安全漏洞打補丁的開發者來說就會非常困擾。

開發者社區已經在尋找相應的替代方案,並且取得了一些進展。這一系列的解決方案現在提供更有效率的IOS APP開發體驗,讓APP開發者去更新他們的代碼只要他們認為是合適的,並且立即把補丁部署到用戶的設備上去。盡管這些技術提供更自由的開發體驗,它們並不符合蘋果試圖去維持的相同的安全規范。更糟糕的是,這些理念可能會成為蘋果APP STORE堅固“城牆”的阿喀琉斯之踵。

在這篇文章中,FireEye手機安全研究員將會分析采用這些可替代方案的IOS APP的安全風險,並且試圖阻止IOS APP生態環境做出意外的安全妥協。

有關這方面在最近了解和學習中我注意到兩種解決方案,一種是常用的-JSPatch,還有一種是iOS滴滴的動態化方案-DynamicCocoa

 

一、JSPATCH

有關JSPatch方面的說明就不多說了,想了解的朋友可以百谷必就能找出一大把,我在此貼個我覺得比較詳細的傳送門:

http://www.tuicool.com/articles/IRr2Ura

本來是烏雲上的原貼 不過烏雲事件相比大家也了解了。。。

JSPatch的使用接入方式也簡單粗暴,兩句代碼完成接入。。而且引擎也是小的很。

1.接入JSPatch官方服務器的方式:

    [JSPatch startWithAppKey:@"0a78sd6f7dsad7f658"];

    [JSPatch sync];

這個APP KEY和正常SDK一樣,需要去官方申請

然后在官方服務器上上傳補丁即可

2.本地測試時接入工程中的方式:

[JSPatch testScriptInBundle];

注意要把工程中的JS文件名稱改成main.js

這樣:

然后就沒有然后了

3.接入項目中相關服務器的方式:

// 開啟引擎

[JPEngine startEngine];

// 傳入相關的JS字符串

[JPEngine evaluateScript:script];

同樣的向服務器發起一次請求,然后檢測兩次文件的MD5值或者是其他值,來判斷是否更新包,其中加密過程由前端和后台人員進行制定

分隔----------------------------------------------------------

在公司體系、人員和制度比較完善的環境下一般會做一個Manager類來專門進行處理這個流程
比如:

1.因為工程需要,將檢測補丁寫在只在程序啟動時:

2.因需要寫在每次程序跳轉到前台時:

 

有關JSPatch的相關使用方法可以參考這篇文章:

http://www.cnblogs.com/jiangshengkai/p/5784422.html

 

以上是JSPatch的一些說明

分隔---------------------------------

二、DynamicCocoa

動態化一直是 App 開發夢寐以求的能力,而在 iOS 環境下,Apple 禁止了在 Main Bundle 外加載和執行的自己的動態庫,所以像 Android 一樣下發原生代碼的方案被堵死。

后來像 React Native、Weex 這樣的基於 Web 標准的跨端方案出現,各大公司都有對其進行嘗試,但對於滴滴現狀,也許並不適合:

滴滴 App 強交互、以地圖為主體、端特異性高;

客戶端人員充足,跨技術棧學習和開發有較大成本;

大量固化 Native 代碼,重寫成本高。

所以我們思考,能不能做一套保持 iOS 原生技術棧、不重寫代碼就神奇的擁有動態化能力的方案呢?

於是,我們設計和實現了一個具有里程碑意義的 iOS 專屬動態化方案:DynamicCocoa

---------孫源

DynamicCocoa初識

DynamicCocoa可以讓現有的Objective-C代碼轉換生成中間代碼(JS),下發后動態執行,相比其他動態化方案,優勢在於:

  • 使用原生技術棧:使用者完全不用接觸到JS或任何中間代碼,保持原生的Objective-C開發、調試方式不變;

  • 無需重寫已有代碼:已有native模塊能很方便的變成動態化插件;

  • 語法支持完備性高:支持絕大多數日常開發中用到的語法,不用擔心這不支持那不支持;

  • 支持HotPatch:改完bug后直接從源碼打出patch,一站式解決動態化和熱修復需求。

不論是動態化還是HotPatch,我們都能讓開發者“Write Cocoa,Run Dynamically”。

ff577fae869247068dd2853a6b65afdb_th.png

語法支持

DynamicCocoa能支持絕大部分日常使用的Objective-C/C語法,挑幾個特殊的:

  • 完整的Class定義:interface、category、classextension、method、property,最重要的是支持完備的ivar定義,保持和native完全一致的實例內存結構;

  • ARC:可以正確處理strong、weak、unsafe_unretained等對象的引用計數,對象的ivar也可以正確的釋放;

  • C函數:支持C函數的定義與C函數的調用、內聯函數的調用;

  • 可變參數:支持C與OC的可變參數方法的調用,如NSLog;

  • struct:支持任意結構體的使用,無需額外處理;

  • block:支持創建和調用任意參數類型的block;

  • 其他OC特性:如@selector、@protocol、@encode、for..in等;

  • 其他C特性:支持使用宏、static變量、全局變量,取地址等。

舉個例子,你可以放心的使用下面的寫法,並能被正確的動態執行:

a67a4ced370444a0bba4a05f2fca2b3e_th.jpeg

資源支持

一個功能模塊,除了代碼外,資源也是必不可少的,DynamicCocoa的動態bundle支持:

  • xib和storyboard;

  • xcassets;

  • 不放在xcassets里的圖片資源;

  • 其他資源文件。

對於習慣於使用IB來開發UI的人來說,這將是一個很好的開發體驗。

工具鏈支持

我們使用Ruby開發了一套命令行工具(類比為xcodebuild),大幅簡化了配置開發環境、OC代碼轉換、資源處理、打包的復雜度,它可以:

  • 解析XcodeProject:讀取工程編譯選項,保持和native編譯參數一致;

  • 增量編譯:緩存JS轉換結果,只重新轉換修改過的文件,大幅提高build速度;

  • 鏈接:分析類依賴,將多個JS按依賴順序合並,提高文件讀取速度;

  • 資源編譯:編譯用到的xib、storyboard和xcassets;

  • 打包:將JS、資源等打包成bundle。

對於開發者來說,就像pod命令一樣,所有操作都可以通過這個命令完成。

動態插件開發流程

首先App中需要集成DynamicCocoaEngineSDK,用來執行下發的bundle開發到發布的流程如下圖所示:

fe86cf549e4647be83b2f4547622dd15_th.jpeg

當然,DynamicCocoa只提供命令行工具和EngineSDK,可以完成本地打包、運行和測試,而線上發布后台、服務端、CDN等需要自行解決。

在滴滴內部,我們構建了開發、Review、線上回歸測試、灰度、發布、回滾、統計的閉環系統,以服務的形式給內部接入。

HotPatch過程

HotPatch本質上是方法粒度上的動態化,所以在整個框架搭建起來后,HotPatch也不難實現,使用DynamicCocoa做熱修復的最大優勢是開發者依然只對源碼負責,修改完bug后,打個patch包,修復成功后把源碼改動直接push到代碼倉庫就行了。

假設我們發現了下面的bug:

706b4b9ace7e42e980b91de252b079f9_th.png

然后在Native進行修復並自測:

36.jpeg

自測完成后,在這個方法后面添加一個神奇的Annotation:

37.jpeg

使用命令行工具在patch模式下進行打包,就能把所有標記了的method提取出來,分別轉換成JS表示,打到一起進行發布。

除了修改一個方法外,patch模式還支持:

  • 調用原方法;

  • 新增一個方法;

  • 新增一個property來輔助修復bug;

  • 新增一個Class。

最后,開發者可以安心的把修改后的代碼(甚至可以保留Annotation)gitpush,完成熱修復工作。

打開黑箱

66.png

就像Objective-C是由Clang編譯器和Objective-CRuntime共同實現一樣,DynamicCocoa也是由對應的兩部分構成:

  • 在Clang的基礎上,實現了一個OC源碼到JS代碼的轉換器;

  • 實現OC-JS互調引擎的DynamicCocoaSDK。

我們知道,Clang-LLVM的標准編譯流程是從源代碼經過預處理、詞法解析、語法解析生成語法樹,CodeGen生成LLVM-IR,進入編譯器后端進行優化和匯編,最終生成目標文件(Mach-O)。

14.png

而我們既希望Clang幫助完成源碼處理的步驟,又希望生成結果是JS表示形式,於是在Clang生成抽象語法樹(AST)后,我們進行接管,實現了一個OC2JSCodeGen,遍歷各個特定語法節點輸出JS表示:

15.png

由於轉換器和Clang前端標准編譯流程相同,所以只要native代碼能build,轉換器就能build,這也是DynamicCocoa能讓動態包和native保持嚴格一致的先決條件。

注:轉換器是基於Clang開發的獨立命令行工具,它的使用並不會對原有的Xcode工程產生任何影響。

另一部分是要集成進App的DynamicCocoaSDK,它的職責是為JS中間代碼提供Runtime環境,實現OC-JS的互調引擎,能夠加載動態bundle,提供便捷的API,整體架構如下:

16.png

其中一些有趣的點:

  • 底層使用libffi來處理各個架構下的callingconventions,實現caller調用棧的構建和callee調用棧的解析,用於實現OC/C函數調用、動態imp、block等。

  • 由於JS的弱類型,數值變量在做計算時很容易丟失類型信息,比如inta=1/2;在OC中表示整除,結果為0,但進入JS就都會按照double計算,結果為0.5,造成了不一致。所以DynamicCocoa接管了JS中的類型信息,強轉或運算符都需要特殊處理。

  • 為了實現block,我們構造了和nativeblock一致的內存結構,不論是JS創建的block還是native傳進JS的block,都可以無差別的調用。

  • 雖然runtime提供了動態創建OCClass的API,但只能創建MRC的Class,導致ARC下ivar並不會乖乖釋放,我們深入到Class和實例真實內存結構中,給動態創建的類增加了ARC能力,並按照Non-FragileABI模擬真實ivar內存布局和ivarlayout編碼,如果你重寫了dealloc方法,DynamicCocoa甚至能夠像native一樣自動調用super。

DynamicCocoa帶來的改變

DynamicCocoa動態化技術給App開發帶來了很大的想象空間:

  • 低成本的動態化:無需額外學習,無需重寫代碼,可以快速的將已有模塊動態化;

  • 協作方式:對於大團隊,發布版本不必再彼此牽制;

  • 功能快速迭代:無需經過審核和AppStore發版,像HTML5一樣隨發隨上;

  • App瘦身:Native只需要留好插件入口,實現由網絡下發,減少App體積;

  • ABTest:不必局限於Native埋進去的AB功能Test,發版后能動態下發各種Test。

相比跨端方案,也帶來了一個新思路:iOS和Android都保留Native開發模式,用各自的方式將Native代碼直接動態化,保持各平台的差異性。

Q&A

與JSPatch有什么區別?

兩者思路上都是實現JS和OC的互調:DynamicCocoa的重點是動態化能力,優勢在於完全不用寫JS和更多的語法特性支持;對於HotPatch來說JSPatch是更加小巧、輕量的解決方案。

這套框架在滴滴App有上線使用么?

有,在滴滴App已經上線並使用了好幾個版本,如滴滴小巴、專車接送機都有過10k級別的動態化模塊上線。

動態包運行的性能是否有很大下降?

動態JS代碼的運行要經過頻繁的JSCore和OC間的切換,性能相比Native必定會有損耗,但經過優化,現在已經達到了無感知的程度:在我們的實際使用中,若不在頁面上添加特定標志,開發者和QA都無法分辨出當前頁面運行的是native還是動態包…后續會有詳細的性能分析和大家分享。

動態包大小如何?

與資源大小和Native源碼量有很大關系,不考慮資源的情況下,量級大概在10000行代碼100KB的動態包。

是否支持多線程?

現在簡單的支持GCD來處理多線程,可以使用dispatch_async將一個block放到另一個queue中執行。

如何定位動態包的Crash?

動態JS代碼運行在JSCore中,並沒有直接獲取調用棧的方式,我們提供了stacktrace功能,將最近調用棧中每個JS到OC/C的互調都記錄下來,在發生Crash時便可以取出來作為附加信息隨Crash日志上報給統計平台,方便問題的定位。

會不會過不了蘋果審核?

市面上很多動態化、HotPatch方案都基於JS的下發,運行在原生JSCore上,相信只要不在審核期間下發動態功能,Apple是不太會拒絕的。

有沒有可能支持Swift直接動態化?

相比OC,Swift的動態化和HotPatch更加有難度,但我們已經有了可行的方案,是可以做到的,只是對於當前滴滴的現狀(絕大多數都在用OC開發),緊急程度並不高,后面再考慮支持。

是否有開源計划?

有,我們正在積極的准備相關事項,於2017年初考慮開源。

分隔-------------------------------以上轉自:http://www.cocoachina.com/ios/20161220/18400.html

 

相比,JSPatch在補丁方面以JavaScript為主,DynamicCocoa以原生代碼為主,已經屬於使用階段,但並未開源,或者說是未普及;

JSPatch需要開發人員需要有一定的JavaScript基礎,DynamicCocoa在此之上做了相應處理,調用原生OC和Swift語言並且打上標簽就可以使用;

我個人不介意了解和使用這兩種技術,畢竟JavaScript使用還是很廣泛,而且Swift的發展有點向進軍服務器的趨勢,而且C#也不甘落后的做出了橋接iOS的SDK

(參考我上篇文章:http://www.cnblogs.com/axclogo/p/6179150.html

以后指不定C#也會推出相應的熱修復功能也不一定。

總結:

市場行情在不斷着發展着新需求,可見出開發也不斷着用各種方法適配着新需求,在開發界只有不斷進步才會不被淘汰。

 

Once a new technology starts rolling, if you’re not part of the steamroller, you’re part of the road.

-------(Stewart Brand)

 

一旦一種新技術開始滾動碾壓道路,如果你不能成為壓路機的一部分,那么你就只能成為道路的一部分

-------Stewart Brand

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM