OCEval-动态执行ObjectiveC的热修复方案

栏目: Objective-C · 发布时间: 5年前

内容简介:目前流行的 JSPatch/RN 基于JavaScriptCore提供了iOS的热修复和动态化方案。但是都必须通过下发Javascript脚本来调用Objective-C。 尤其是JSPatch,编写大量的JS代码来调用OC的方法,开发效率较低(目前可以借助语法转换器),运行效率也会打折扣。 更好的方案是直接编写Objective-C代码,来实现热修复或者动态化方案。开发效率更高,代码的执行效率也更高。在python和javascript等脚本语言里,有类似eval()函数来直接动态执行代码。所以我实现了

目前流行的 JSPatch/RN 基于JavaScriptCore提供了iOS的热修复和动态化方案。但是都必须通过下发Javascript脚本来调用Objective-C。 尤其是JSPatch,编写大量的JS代码来调用OC的方法,开发效率较低(目前可以借助语法转换器),运行效率也会打折扣。 更好的方案是直接编写Objective-C代码,来实现热修复或者动态化方案。开发效率更高,代码的执行效率也更高。

python 和javascript等脚本语言里,有类似eval()函数来直接动态执行代码。所以我实现了 OCEval 这个库,让我们能直接动态执行Objective-C代码。例子如下:

NSString *inputStr = @"return 1 + 3 <= 4 && [NSString string] != nil;";
NSNumber *result = [OCEval eval:inputStr]; // result: @(YES)
复制代码

为了实现跟JSPatch类似的热修复功能,增加了方法替换。我们就可以通过下发Objective-C代码进行现有App的方法替换,来进行热修复的功能。

//在新的imp里直接调用旧的方法实现
NSString *viewDidLoad2 = @"{\
[originalInvocation invoke];\
";

[OCEval hookClass:@"ViewController"
         selector:@"viewDidLoad"
         argNames:@[]
          isClass:NO
   implementation:viewDidLoad2];
复制代码

OCEval甚至可以用来完整的编写一个页面或者App,并动态下发。我在iOS的Demo里实现了一个简单的页面,具体见源码。

实现原理

C和C++的性能高,是因为编译型语言在编译期就已经生成了机器码,运行时只需要执行机器码所以执行效率高,但是动态性差。 js的性能差,是因为js的runtime引擎通常是在实际执行前进行的编译的。优点是动态性好。 像Dart和python等等都可以编译打包执行或者JIT(Just in time)执行。

不同于C和C++,Objective-C是动态化的语言,Objective-C的runtime利用消息发送和转发可以动态地执行任何方法。 同时Objective-C又不同于javascript等完全动态化的语言。 因为大多数调用是在编译期就已经决定的,编译出可执行文件(mach-O)。

所以在OCEval里实现了一个轻量级的解释器,动态地解释Objective-C代码,同时利用OC的runtime消息转发来动态执行Objective-C的代码,就可以实现类似eval()函数的完全动态化方式。

解释器

Objective-C在LLVM下的编译过程:

源码 -> AST -> LLVM IR(中间语言) -> LLVM Bytecode -> ASM -> Native

LLVM的前端是Clang,Clang的工作是把源码变成AST语法生成树。

Clang的前端编译过程:

Preprocesser
Lexer
Parser

但是在OCEval里,没有做得那么复杂,因为只是为了能够执行。所以只实现了词法分析和语法分析,得到语法生成树AST。

Runtime

执行的时候递归下降地执行每一条指令。这里利用的runtime主要是NSInvocation,利用methodSignature封装方法的调用惯例,跟JSPatch/RN的最终调用方式如出一辙。

+ (id)invokeWithCaller:(id)caller selectorName:(NSString *)selectorName argments:(NSArray *)arguments
{
    SEL selector = NSSelectorFromString(selectorName);
    NSInvocation *invocation;
    NSMethodSignature *methodSignature = [caller methodSignatureForSelector:selector];
    invocation= [NSInvocation invocationWithMethodSignature:methodSignature];
    [invocation setTarget:caller];
    [invocation setSelector:selector];
    NSUInteger numberOfArguments = methodSignature.numberOfArguments;
    NSInteger inputArguments = [arguments count];
    if (inputArguments > numberOfArguments - 2) {
        id result = invokeVariableParameterMethod(arguments, methodSignature, caller, selector); //转而调用objc_msgsend
        return result;
    }
    return [self invokeWithInvocation:invocation argments:arguments];
}
复制代码

参考JSPatch,在类似 [NSString stringWithFormat:] 这样可变参数的方法里使用 objc_msgsend 。因为 NSInvocation 不支持不确定的参数个数的情况。

性能

因为省去了跟JavascriptCore进行参数传递的过程,单个方法调用比JSPatch/RN快100%,耗时只有JSPatch一半,多个方法调用优势更大,耗时可能只有30%以下。

NSInvocation的调用跟Native速度差不多。但是因为动态调用很麻烦,入参出参和调用惯例都需要动态定义,同时上下文参数在内存的传递也比较慢,所以整体是比原生慢很多(动态化必要的牺牲)。


以上所述就是小编给大家介绍的《OCEval-动态执行ObjectiveC的热修复方案》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Python高性能编程

Python高性能编程

【美】 戈雷利克 (Micha Gorelick)、【美】 欧日沃尔德(Ian Ozsvald) / 人民邮电出版社 / 2017-7-1 / 79

本书共有12章,围绕如何进行代码优化和加快实际应用的运行速度进行详细讲解。本书主要包含以下主题:计算机内部结构的背景知识、列表和元组、字典和集合、迭代器和生成器、矩阵和矢量计算、并发、集群和工作队列等。最后,通过一系列真实案例展现了在应用场景中需要注意的问题。 本书适合初级和中级Python程序员、有一定Python语言基础想要得到进阶和提高的读者阅读。一起来看看 《Python高性能编程》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

随机密码生成器
随机密码生成器

多种字符组合密码

SHA 加密
SHA 加密

SHA 加密工具