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

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

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

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

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

 

1、JSPATCH後端

有關JSPatch方面的說明就很少說了,想了解的朋友能夠百穀必就能找出一大把,我在此貼個我以爲比較詳細的傳送門:xcode

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的一些說明

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

2、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

相關文章
相關標籤/搜索