V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
dKingbin
V2EX  ›  iOS

iOS 动态化热修复方案

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

    前言

    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 功能非常强悍,是获取函数指针的最优解。 第一种局限性非常大,但是也是最安全的方法,应该也是能被苹果所接受的一种方法。为什么这么说呢? 我们所追求的是能够动态运行所有的 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   ·   实用小工具   ·   3344 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 90ms · UTC 12:43 · PVG 20:43 · LAX 04:43 · JFK 07:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.