iOS 动态化热修复方案 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
dKingbin
V2EX    iOS

iOS 动态化热修复方案

  •  
  •   dKingbin 2019-07-31 11:28:32 +08:00 5922 次点击
    这是一个创建于 2293 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    iOS 热修复方案经过 JSPatch 事件后,也消停了很久。bang 神在《 JSPatch 动态更新 iOS APP 》中曾提到,为了更符合 Apple 的规则,即《 pple 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 功能非常强悍,是获取函数指针的最优解。 第一种局限性非常大,但是也是最安全的方法,应该也是能被苹果所接受的一种方法。为什么这么说呢? 我们所追求的是能够动态运行所有的 OC 语法,加上部分 C 函数。因此在映射表中,只需要加上下面 runtime 常用的 C 函数映射, 我们就能够动态运行所有的 OC 语言。然后再自定义需要用到的 C 函数,基本上能够满足绝大部分热更新的需要,同时也能够被苹果所接受。

    class_addMethod NSSelectorFromString objc_setAssociatedObject objc_getAssociatedObject class_getMethodImplementation ... 

    采用 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); 

    已支持语法

    • [x] if/else while do/while for
    • [x] return break continue
    • [x] i++ i-- ++i --i
    • [x] +i -i !i
    • [x] + - * / %等四则运算
    • [x] >> << & | ^ 等位运算
    • [x] && || >= <= != > < 等比较运算
    • [x] ?:
    • [x] __block
    • [x] array[i] dict[@""]
    • [x] @666 @() @[] @{}
    • [x] self super
    • [x] self.property
    • [x] self->_property
    • [x] 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

    参考链接

    JSPatch 动态更新 iOS APP

    iOS 动态化的故事

    Apple Developer Program License Agreement

    滴滴 iOS 动态化方案 DynamicCocoa 的诞生与起航

    第 1 条附言    2019-08-01 10:40:26 +08:00

    Warnning

    纯粹是技术分享,不要用于上架操作! 
    5 条回复    2019-09-01 13:30:44 +08:00
    loginbygoogle
        1
    loginbygoogle  
       2019-07-31 11:50:18 +08:00 via Android
    我看你是想下架整改整改了
    ooops
        2
    ooops  
       2019-07-31 12:29:39 +08:00
    厉害厉害。不过你下发的还是可执行代码。“我们就能够动态运行所有的 OC 语言。然后再自定义需要用到的 C 函数,基本上能够满足绝大部分热更新的需要,同时也能够被苹果所接受。”不知道最后的结论是怎么得出来的。。
    Sricecake
        3
    Sricecake  
       2019-07-31 18:50:38 +08:00
    苹果目的是不希望有功能不经过审核就被用户使用,和你用什么技术无关。热更新和苹果审核天然是冲突的,不是你选择用那种技术方案就能解决的。
    hanangellove
        4
    hanangellove  
       2019-07-31 22:07:52 +08:00
    ,为啥要用热更新。。。
    技术说为了不用审核就可以改 bug,
    产品说为了不用审核就可以改功能,
    领导说要敏捷开发,快速迭代,
    老板说我们外部竞争激烈,要快速适应新的市场环境。

    。。。
    程序员操碎了心,遂卒
    xi_lin
        5
    xi_lin  
       2019-09-01 13:30:44 +08:00
    应该移去 iDev 板块的。不过有试过这个能上架么?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     941 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 18:27 PVG 02:27 LAX 10:27 JFK 13:27
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86