iOS動態化熱修復方案

NO IMAGE

iOS 動態化熱修復方案

Warnning

純粹是技術分享,歡迎到github點贊。
https://github.com/dKingbin/DynamicOC

前言

iOS熱修復方案經過JSPatch事件後,也消停了很久。bang神在《JSPatch – 動態更新iOS APP》中曾提到,為了更符合Apple的規則,即《Apple Developer Program License Agreement》 裡3.3.2提到的不可動態下發可執行代碼。
JSPatch特地繞了js的圈子,從而實現曲線救國、實現熱更新的方案。但是事實證明了Apple對於這種方案也是不認可的,根本的原因還是在於JSPath做得太過極致–支持絕大部分的OC/C語法。

思考

既然JSPatch繞道js的方法,已經被Apple拒絕了,那麼就再次回到原點,重新出發。新的框架或者新的方案我覺得至少有一個充分條件,就是不能太極致。
Objective-C作為一種動態的語言,因此能夠動態執行所有OC語法是正常的,Aspects類似的框架也是被Apple認可的。至於是否需要運行所有的C函數,這個有待商榷。
第二個方面,則是放棄javascript/lua等類似語言作為更新的腳本,而是採用原生的Objective-C作為更新的腳本語言。

動態運行C函數

C語言是沒有反射機制的,作為一門編譯型語言,在編譯期間就已經生成機器碼。因此如果要從字符串中獲取到對應的函數指針,那麼大概有兩種方法:

  • 建立映射表, 將函數名和函數指針建立一個映射表。
  • dlsym, 根據動態鏈接庫操作句柄與符號,返回符號對應的地址。

第二種是目前JSPatch採用的辦法,當然也被Apple警告了。dlsym功能非常強悍,是獲取函數指針的最優解。
第一種侷限性非常大,但是沒有用到黑魔法。

採用Objective-C作為更新的腳本語言

通過flex/yacc,直接解析Objective-C語法,不再採取js/lua等腳本語言。

DynamicOC

經過上面的思考,在最近業餘中做了DynamicOC的項目,百分百原生支持採用Objective-C作為更新的腳本語言。
當然動態運行C函數還是採用dlsym獲取函數指針的辦法,後面會逐步改為映射表的做法。

原理

DynamicOC使用flex/yacc進行詞法解析和語法分析,轉為一顆語法生成樹AST。
然後通過解析每個節點,從而執行相應的代碼。因為採用的是Objective-C作為腳本語言,因此極容易適配。

功能特點

  • 動態執行OC代碼
  • 動態執行C函數和block異步調用
  • 動態添加屬性
  • 動態替換方法
  • 動態添加方法
  • 有完善的單元測試
  • flex/yacc實現強大的OC語法解析器
  • 支持CGRect/CGSize/CGPoint/NSRange/UIEdgeInsets/CGAffineTransform常用結構體

基本用法

動態執行block

NSString* text = @" \
__block int result = 0;\
UIView* view = [[UIView alloc]init];\
void(^blk)(int value) = ^(int value){\
view.tag = value;\
};\
blk(1024);\
return view.tag;";
ASTNode* root = [ASTUtil parseString:text];
ASTVariable* result = [root execute];
NSAssert([result.value doubleValue] == 1024, nil);

動態執行C函數

int echo(int value) {
return value;
}
NSString* text = @" \
[OCCfuntionHelper defineCFunction:@\"echo\" types:@\"int, int\"]; \
return echo(1024);";
ASTNode* root = [ASTUtil parseString:text];
ASTVariable* result = [root execute];
NSAssert([result.value doubleValue] == 1024, nil);

動態添加Property

NSString* text = @" \
[OCCfuntionHelper defineCFunction:@\"objc_setAssociatedObject\" types:@\"void,id,void *,id,unsigned int\"];\
[OCCfuntionHelper defineCFunction:@\"objc_getAssociatedObject\" types:@\"id,id,void *\"];\
NSString* key = @\"key\"; \
objc_setAssociatedObject(self, key, @(1024), 1);\
return objc_getAssociatedObject(self, key);";
ASTNode* root = [ASTUtil parseString:text];
ASTVariable* result = [root execute];
NSAssert([result.value doubleValue] == 1024, nil);

已支持語法

  • if/else while do/while for
  • return break continue
  • i++ i– ++i –i
  • +i -i !i
  • + – * / %等四則運算
  • >> << & | ^ 等位運算
  • && || >= <= != > < 等比較運算
  • ?:
  • __block
  • array[i] dict[@””]
  • @666 @() @[] @{}
  • self super
  • self.property
  • self->_property
  • most of objective-c keyword

TODO

  • @available()
  • [NSString stringWithFormat:”%d”,value] : use [NSString stringWithFormat:”%@”,@(value)] instead。
  • dispatch_async / dispatch_after …
  • *stop =YES, in block
  • fix bugs

Github鏈接在此: DynmaicOC, 覺得有幫助的可以點個星哦!謝謝!

參考鏈接

JSPatch – 動態更新iOS APP

iOS 動態化的故事

Apple Developer Program License Agreement

滴滴 iOS 動態化方案 DynamicCocoa 的誕生與起航

相關文章

淺談瀏覽器與node中的事件循環

手把手帶你實現一個符合promiseA+規範的promise類庫

對於元素是非基本類型的數組去重

UC面試總結