JSPatch,只需在项目中引入极小的引擎,就可以使用JavaScript调用任何Objective-C的原生接口,获得脚本语言的能力:动态更新APP,替换项目原生代码修复bug。

用途

是否有过这样的经历:新版本上线后发现有个严重的bug,可能会导致crash率激增,可能会使网络请求无法发出,这时能做的只是赶紧修复bug然后提交等待漫长的AppStore审核,再盼望用户快点升级,付出巨大的人力和时间成本,才能完成此次bug的修复。

使用JSPatch可以解决这样的问题,只需在项目中引入JSPatch,就可以在发现bug时下发JS脚本补丁,替换原生方法,无需更新APP即时修复bug。

例子

@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
//JS
defineClass("JPTableViewController", {
//instance method definitions
tableView_didSelectRowAtIndexPath: function(tableView, indexPath) {
var row = indexPath.row()
if (self.dataSource().length > row) { //加上判断越界的逻辑
var content = self.dataArr()[row];
var ctrl = JPViewController.alloc().initWithContent(content);
self.navigationController().pushViewController(ctrl);
}
}
}, {})

这样 JPTableViewController 里的 -tableView:didSelectRowAtIndexPath: 就替换成了这个JS脚本里的实现,在用户无感知的情况下修复了这个bug。

更多的使用文档和demo请参考github项目主页

原理

JSPatch用iOS内置的JavaScriptCore.framework作为JS引擎,但没有用它JSExport的特性进行JS-OC函数互调,而是通过Objective-C Runtime,从JS传递要调用的类名函数名到Objective-C,再使用NSInvocation动态调用对应的OC方法。详细的实现原理以及实现过程中遇到的各种坑和hack方法会另有文章介绍。

方案对比

目前已经有一些方案可以实现动态打补丁,例如WaxPatch,可以用Lua调用OC方法,相对于WaxPatch,JSPatch的优势是:

1.JS语言
JS比Lua在应用开发领域有更广泛的应用,目前前端开发和终端开发有融合的趋势,作为扩展的脚本语言,JS是不二之选。

2.符合Apple规则
JSPatch更符合Apple的规则。iOS Developer Program License Agreement里3.3.2提到不可动态下发可执行代码,但通过苹果JavaScriptCore.framework或WebKit执行的代码除外,JS正是通过JavaScriptCore.framework执行的。

3.小巧
使用系统内置的JavaScriptCore.framework,无需内嵌脚本引擎,体积小巧。

4.支持block
wax在几年前就停止了开发和维护,不支持Objective-C里block跟Lua程序的互传,虽然一些第三方已经实现block,但使用时参数上也有比较多的限制。

相对于WaxPatch,JSPatch劣势在于不支持iOS6,因为需要引入JavaScriptCore.framework。另外目前内存的使用上会高于wax,持续改进中。

风险

JSPatch让脚本语言获得调用所有原生OC方法的能力,不像web前端把能力局限在浏览器,使用上会有一些安全风险:

1.若在网络传输过程中下发明文JS,可能会被中间人篡改JS脚本,执行任意方法,盗取APP里的相关信息。可以对传输过程进行加密,或用直接使用https解决。

2.若下载完后的JS保存在本地没有加密,在未越狱的机器上用户也可以手动替换或篡改脚本。这点危害没有第一点大,因为操作者是手机拥有者,不存在APP内相关信息被盗用的风险。若要避免用户修改代码影响APP运行,可以选择简单的加密存储。

其他用途

JSPatch可以动态打补丁,自由修改APP里的代码,理论上还可以完全用JSPatch实现一个业务模块,甚至整个APP,跟wax一样,但不推荐这么做,因为:

  1. JSPatch和wax一样都是通过Objective-C Runtime的接口通过字符串反射找到对应的类和方法进行调用,这中间的字符串处理会损耗一定的性能,另外两种语言间的类型转换也有性能损耗,若用来做一个完整的业务模块,大量的频繁来回互调,可能有性能问题。
  2. 开发过程中需要用OC的思维写JS/Lua,丧失了脚本语言自己的特性。
  3. JSPatch和wax都没有IDE支持,开发效率低。

若想动态为APP添加模块,目前React Native给出了很好的方案,解决了上述三个问题:

  1. JS/OC不会频繁通信,会在事件触发时批量传递,提高效率。(详见React Native通信机制详解
  2. 开发过程无需考虑OC的感受,遵从React框架的思想进行纯JS开发就行,剩下的事情React Native帮你处理好了。
  3. React Native连IDE都准备好了。

所以动态添加业务模块目前还是推荐尝试React Native,但React Native并不会提供原生OC接口的反射调用和方法替换,无法做到修改原生代码,JSPatch以小巧的引擎补足这个缺口,配合React Native用统一的JS语言让一个原生APP时刻处于可扩展可修改的状态。

目前JSPatch处于开发阶段,稳定性和功能还存在一些问题,欢迎大家提建议/bug/PR,一起来做这个项目。

来自:http://blog.cnbang.net/works/2767

JSPatch动态更新APP的更多相关文章

  1. JSPatch – 动态更新iOS APP

    原文:http://blog.cnbang.net/works/2767/ JSPatch是最近业余做的项目,只需在项目中引入极小的引擎,就可以使用JavaScript调用任何Objective-C的 ...

  2. JSPatch 动态更新,bug修复

    本文贴出项目中热修复的代码片段: require('UIView, JPObject, HtmlAllViewController,DataManager,EMClient,EaseMessageVi ...

  3. IOS App动态更新

    框架 JSPatch WaxPatch react-native   方案对比 目前已经有一些方案可以实现动态打补丁,例如WaxPatch,可以用Lua调用OC方法,相对于WaxPatch,JSPat ...

  4. 微软Azure配置中心 App Configuration (三):配置的动态更新

    写在前面 我在前文: <微软Azure配置中心 App Configuration (一):轻松集成到Asp.Net Core>已经介绍了Asp.net Core怎么轻易的接入azure ...

  5. Android中动态更新ListView(转)

    在使用ListView时,会遇到当ListView列表滑动到最底端时,添加新的列表项的问题,本文通过代码演示如何动态的添加新的列表项到ListView中.实现步骤:调用ListView的setOnSc ...

  6. iOS 利用 Framework 进行动态更新

    http://nixwang.com/2015/11/09/ios-dynamic-update/ 前言 目前 iOS 上的动态更新方案主要有以下 4 种: HTML 5 lua(wax)hotpat ...

  7. 动态更新Toolbar Menu以及Menu中同时显示文字和图标

    动态更新Toolbar Menu以及Menu中同时显示文字和图标 我们经常会有这样的需求,在切换Fragment或者点击某个按钮后动态更新Toolbar上Menu项.但是onCreateOptions ...

  8. Android Handler传递参数动态更新UI界面demo

    package com.example.demo_test; import android.app.Activity; import android.os.Bundle; import android ...

  9. Android ListView动态更新数据

    ListView就是可以显示一行行Item的控件,有时候数据非常多,通常需要分页显示,但为了减少用户的输入,我们可以动态更新ListView,把下一页要显示的数据的添加到当前ListView中. 先看 ...

随机推荐

  1. 学以致用四----centos7.2 安装python3.6

    基础配置弄好后,开始搭建环境 本次的目的是安装 python 3.6 一.安装相关的依赖包 在之前的博客里有安装python3.6的步骤,为了避免做重复的步骤.需事先准备好依赖包 yum instal ...

  2. Android自定义视图一:扩展现有的视图,添加新的XML属性

    这个系列是老外写的,干货!翻译出来一起学习.如有不妥,不吝赐教! Android自定义视图一:扩展现有的视图,添加新的XML属性 Android自定义视图二:如何绘制内容 Android自定义视图三: ...

  3. C#-ado.net学习笔记-会有更新

    ado.net 通用类对象.在本地内存暂存数据 托管类对象.让本地通用类对象连接数据库,让本地通用类对象和数据库同步 连接数据库 new connection(connectstring) comma ...

  4. X Window(远程桌面)

    X Window在位映射屏幕上的一个或多个窗口中运行程序.用户可以在每个窗口中同时运行多个程序,并且可以通过用鼠标在窗口之间进行切换. x服务器的程序在本地工作站上运行,并且管理它的窗口和程序. 每个 ...

  5. 【repost】Chrome 控制台console的用法

    下面我们来看看console里面具体提供了哪些方法可以供我们平时调试时使用. 目前控制台方法和属性有: ["$$", "$x", "dir" ...

  6. STL容器(C11)--unordered_map用法

    http://www.cplusplus.com/reference/unordered_map/unordered_map/

  7. php email

    项目中用到了发邮件的功能.代码很简单,但是其中碰到了一些奇怪的问题. smtp error: could not authenticate 163 ======================== 新 ...

  8. jdbc的配置(更新中)

    MySQL的 JDBC URL 格式 for  Connector/J 如下例: 格式如下: jdbc:mysql://[host][,failoverhost...][:port]/[databas ...

  9. django后台admin管理布局

    在model模块里设置 class pc_info(models.Model): ip = models.CharField(max_length=64) sn = models.CharField( ...

  10. VARCHAR的最大长度的问题

    大家知道,在SQL Server 2000中,VARCHAR的最大长度是8000,如果字符串的长度超过8000,保存在VARCHAR中时就会被截断.如果你需要传入的参数恰好很长,比如是一个xml,很多 ...