JSPatch简介 – 动态更新iOS APP
来源:互联网 发布:金融数据挖掘python 编辑:程序博客网 时间:2024/05/22 12:30
1.用途
是否有过这样的经历:新版本上线后发现有个严重的bug,可能会导致crash率激增,可能会使网络请求无法发出,这时能做的只是赶紧修复bug然后提交等待漫长的AppStore审核,再盼望用户快点升级,付出巨大的人力和时间成本,才能完成此次bug的修复。
使用JSPatch可以解决这样的问题,只需在项目中引入JSPatch,就可以在发现bug时下发JS脚本补丁,替换原生方法,无需更新APP即时修复bug。
2.例子
@implementation JPTableViewController- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ NSString *content = self.dataSource[[indexPath row]]; //可能会超出数组范围导致crash JPViewController *ctrl = [[JPViewController alloc] initWithContent:content]; [self.navigationController pushViewController:ctrl];}@end
上述代码中取数组元素处可能会超出数组范围导致crash。如果在项目里引用了JSPatch,就可以下发JS脚本修复这个bug:
import "JPEngine.m"@implementation AppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ [JPEngine startEngine]; [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://cnbang.net/bugfix.JS"]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { NSString *script = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; if (script) { [JPEngine evaluateScript:script]; }}]; return YES;}@end//JSdefineClass("JPTableViewController", { //instance method definitions tableView_didSelectRowAtIndexPath: function(tableView, indexPath) { var row = indexPath.row() if (self.dataSource().length > row) { //加上判断越界的逻辑 var content = self.dataSource()[row]; var ctrl = JPViewController.alloc().initWithContent(content); self.navigationController().pushViewController(ctrl); } }}, {})
这样 JPTableViewController 里的 -tableView:didSelectRowAtIndexPath: 就替换成了这个JS脚本里的实现,在用户无感知的情况下修复了这个bug。
更多的使用文档和demo请参考[github项目主页]。
3.原理
简单来说,JSPatch用iOS内置的JavaScriptCore.framework作为JS引擎,但没有用它JSExport的特性进行JS-OC函数互调,而是通过Objective-C Runtime,从JS传递要调用的类名函数名到Objective-C,再使用NSInvocation动态调用对应的OC方法。
3.1基础原理
能做到通过JS调用和改写OC方法最根本的原因是 Objective-C 是动态语言,OC上所有方法的调用/类的生成都通过 Objective-C Runtime 在运行时进行,我们可以通过类名/方法名反射得到相应的类和方法:
Class class = NSClassFromString("UIViewController");id viewController = [[class alloc] init];SEL selector = NSSelectorFromString("viewDidLoad");[viewController performSelector:selector];
也可以替换某个类的方法为新的实现:
static void newViewDidLoad(id slf, SEL sel) {}class_replaceMethod(class, selector, newViewDidLoad, @"");
理论上你可以在运行时通过类名/方法名调用到任何OC方法,替换任何类的实现。所以 JSPatch 的原理就是:JS传递字符串给OC,OC通过 Runtime 接口调用和替换OC方法。
3.2方法调用
require('UIView')var view = UIView.alloc().init()view.setBackgroundColor(require('UIColor').grayColor())view.setAlpha(0.5)
引入JSPatch后,可以通过以上JS代码创建了一个 UIView 实例,并设置背景颜色和透明度。
3.3方法替换
JSPatch 可以用 defineClass 接口任意替换一个类的方法,用一种hack方式实现。
OC上,每个类都是这样一个结构体:
struct objc_class { struct objc_class * isa; const char *name; …. struct objc_method_list **methodLists; /*方法链表*/};
其中 methodList 方法链表里存储的是Method类型:
typedef struct objc_method *Method;typedef struct objc_ method { SEL method_name; char *method_types; IMP method_imp;};
Method 保存了一个方法的全部信息,包括SEL方法名,type各参数和返回值类型,IMP该方法具体实现的函数指针。
通过 Selector 调用方法时,会从 methodList 链表里找到对应Method进行调用,这个 methodList 上的的元素是可以动态替换的,可以把某个 Selector 对应的函数指针IMP替换成新的,也可以拿到已有的某个 Selector 对应的函数指针IMP,让另一个 Selector 跟它对应,Runtime 提供了一些接口做这些事,以替换 UIViewController 的 -viewDidLoad: 方法为例:
static void viewDidLoadIMP (id slf, SEL sel) { JSValue *jsFunction = …; [jsFunction callWithArguments:nil];}Class cls = NSClassFromString(@"UIViewController");SEL selector = @selector(viewDidLoad);Method method = class_getInstanceMethod(cls, selector);//获得viewDidLoad方法的函数指针IMP imp = method_getImplementation(method)//获得viewDidLoad方法的参数类型char *typeDescription = (char *)method_getTypeEncoding(method);//新增一个ORIGViewDidLoad方法,指向原来的viewDidLoad实现class_addMethod(cls, @selector(ORIGViewDidLoad), imp, typeDescription);//把viewDidLoad IMP指向自定义新的实现class_replaceMethod(cls, selector, viewDidLoadIMP, typeDescription);
这样就把 UIViewController 的 -viewDidLoad 方法给替换成我们自定义的方法,APP里调用 UIViewController 的 viewDidLoad 方法都会去到上述 viewDidLoadIMP 函数里,在这个新的IMP函数里调用JS传进来的方法,就实现了替换 -viewDidLoad 方法为JS代码里的实现,同时为 UIViewController 新增了个方法 -ORIGViewDidLoad 指向原来 viewDidLoad 的IMP,JS可以通过这个方法调用到原来的实现。
4.安全部署RSA校验
RSA校验属于数字签名,用了跟 HTTPS 一样的非对称加密,只是简化了,把非对称加密只用于校验文件,而不解决传输过程中数据内容泄露的问题,而我们的目的只是防止传输过程中数据被篡改,对于数据内容泄露并不是太在意。整个校验过程如下:
- 服务端计算出脚本文件的 MD5 值,作为这个文件的数字签名。
- 服务端通过私钥加密第 1 步算出的 MD5 值,得到一个加密后的 MD5 值。
- 把脚本文件和加密后的 MD5 值一起下发给客户端。
- 客户端拿到加密后的 MD5 值,通过保存在客户端的公钥解密。
- 客户端计算脚本文件的 MD5 值。
- 对比第 4/5 步的两个 MD5 值(分别是客户端和服务端计算出来的 MD5 值),若相等则通过校验。
只要通过校验,就能确保脚本在传输的过程中没有被篡改,因为第三方若要篡改脚本文件,必须计算出新的脚本文件 MD5 并用私钥加密,客户端公钥才能解密出这个 MD5 值,而在服务端未泄露的情况下第三方是拿不到私钥的。
最后有个小问题,保存在客户端的代码也可能被人篡改,需不需要采取措施?这个要看各人需求了,因为这个安全问题不大,能篡改本地文件,差不多已经有手机所有权限了,这时也无所谓脚本会不会被篡改了。若有需要,可以加个简单的对称加密,或者按上述流程每次都验证一遍MD5值。
- JSPatch简介 – 动态更新iOS APP
- JSPatch – 动态更新iOS APP
- JSPatch – 动态更新iOS APP
- JSPatch – 动态更新iOS APP
- JSPatch – 动态更新iOS APP
- JSPatch – 动态更新iOS APP
- JSPatch – 动态更新iOS APP
- JSPatch – 动态更新iOS APP
- JSPatch – 动态更新iOS APP
- JSPatch – 动态更新iOS APP
- JSPatch – 动态更新iOS APP
- JSPatch – 动态更新iOS APP
- JSPatch-动态更新IOS APP
- JSPatch - 动态更新iOS APP
- JSPatch: iOS App 动态更新服务平台
- 使用JSPatch实现iOS APP动态更新
- iOS 动态改变线上app JSPatch技术
- 利用JSPatch跳过AppStore审核,动态更新APP
- hive介绍
- CMake 手册详解(七)
- 文件相似度比对工具的设计与实现
- 8天学通MongoDB——第一天 基础入门
- (c++)输入两个正整数m和n,求其最大公约数和最小公倍数。
- JSPatch简介 – 动态更新iOS APP
- Java NIO示例:多人网络聊天室
- get_hash_entry
- 索引文件浅析
- OpenSSL 与 SSL 数字证书概念贴
- Vue.js教程6-列表渲染
- Android-EditText只能输入不能删除的问题
- POJ 2362 Square
- 后缀数组(重复次数最多的连续重复子串)