JSPatch 热修复技术

1,210 阅读4分钟

JSPatch 为我们的 iOS 程序提供了一个动态修改代码的能力。比如你的 APP 发生了线上 Bug,通过 JSPatch 不需要重新发版,就可以立即修复。

客户端开发的痛点

众所周知,我们做 APP 客户端开发一个很大的痛点就是,如果我们的 APP 在商店发布后,如果发生了比较验证的 bug, 我们是没有办法立即修复的。 对于 App Store 而言,最快也就是给商店递交紧急审核的请求,但这也需要重新发版,并且用户要更新 APP 后才可以修复。

随着 APP 开发生态越来越趋于成熟,相关的各种问题都有了比较好的解决方案。那么对于动态修复技术,也是一样。 JSPatch 就是解决这个问题的一个开源库。它包含一个解析引擎,可以将符合规则的 js 脚本,解析成 iOS 原生代码。这样我们就可以在不重新发布新版的情况下,修复我们线上的问题了。

JSPatch 简介

JSPatch 的使用非常简单,我们初始化 JS 解析引擎,然后传入脚本文件:

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{
    [JPEngine startEngine];
    NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
    NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil];
    [JPEngine evaluateScript:script];
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    [self.window addSubview:[self genView]];
    [self.window makeKeyAndVisible];
    return YES;
}
- (UIView *)genView
{
    return [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 320)];
}
@end

上面这段代码应该不难理解,首先我们调用 [JPEngine startEngine] 方法。接着找到 JS 脚本文件,并调用 [JPEngine evaluateScript:script] 执行这个脚本。

这里我们的脚本文件是存放在 Bundle 中的。 但这个脚本其实可以来自任何地方,包括从服务器中读取。这就为我们提供了动态下发脚本的能力。在任何时候,我们都可以下发脚本。

我们上面的代码中,还定义了一个 genView 方法,这个方法会返回一个 UIView 对象。这个方法正是我们要用 JSPatch 替换掉的。

再来看看 demo.js 文件:

// demo.js
require('UIView, UIColor, UILabel')
defineClass('AppDelegate', {
  // replace the -genView method
  genView: function() {
    var view = self.ORIGgenView();
    view.setBackgroundColor(UIColor.greenColor())
    var label = UILabel.alloc().initWithFrame(view.frame());
    label.setText("JSPatch");
    label.setTextAlignment(1);
    view.addSubview(label);
    return view;
  }
});

这个脚本的语法看起来或许会有些陌生。但我们稍加分析,也不难理解。 首先用 require(‘UIView, UIColor, UILabel’) 导入我们要使用的组件。

然后,后面的 defineClass 方法,定义我们要对哪个类的内容进行替换。 这里我们替换了 AppDelegate 的 genView 方法。 然后再看看脚本中如何定义的 genView 函数体。

首先调用 self.ORIGgenView() 方法,稍微推理一下也容易明白,ORIG 是一个通用前缀,代表最初的方法,ORIGgenView 就是我们在上面 ObjC 代码中定义的 genView 方法了。

也就是说,这里先调用了我们最初的 genView 方法。 然后将它返回的 UIView 对象进行了一些列操作, 添加了一个 UILabel 到它上面。 最后再返回。

好了,现在如果运行这个 APP,实际上我们 genView 方法得到的就是脚本中描述的那样,一个 UIView 包含了一个 UILabel。 而不是 ObjC 中那个原始的方法了。

实践方案

正如上面例子中那样,JSPatch 可以在任何时候加载脚本文件。 它的原理大概就是基于 ObjC Runtime 的机制。将我们的代码在运行时进行替换。

将脚本直接放到 Bundle 里只是我们这个例子为了实验性目的才这样做。 一般来说 JSPatch 更多用于在线上动态进行修复。我们可以将脚本放到服务器中,本地 APP 去服务器中获取脚本,然后执行。

但要格外注意一点,就是脚本的传输安全。一件事情有利就会有弊。 虽然 JSPatch 给我们提供了非常强大的动态修复能力。但因为脚本可以通过远程动态下发,我们就要对安全性更加谨慎了。比如传输协议一定要使用 HTTPS,加大劫持难度。否则一些用户就有可能受到恶意攻击的伤害。

结语

JSPatch 总体上给我们提供了很强的动态修复能力。这点对于用户量比较大的 APP 就很有作用。 但大家在使用的时候,也要对安全实践更加注意。并且还是要将它用在适合它的场景之下,这样你的 APP 的效率和能力都会有很大的提升。

更多内容,大家可以参考 JSPatch 的 Github 主页 github.com/bang590/JSP…

如果你觉得这篇文章有帮助,还可以关注微信公众号 swift-cafe,会有更多我的原创内容分享给你~

本站文章均为原创内容,如需转载请注明出处,谢谢。