[bang's blog]&JSPatch – 动态更新iOS APP
[转载]JSPatch – 动态更新iOS APP
JSPatch是最近业余做的项目,只需在项目中引入极小的引擎,就可以使用JavaScript调用任何Objective-C的原生接口,获得脚本语言的能力:动态更新APP,替换项目原生代码修复bug。
用途
是否有过这样的经历:新版本上线后发现有个严重的bug,可能会导致crash率激增,可能会使网络请求无法发出,这时能做的只是赶紧修复bug然后提交等待漫长的AppStore审核,再盼望用户快点升级,付出巨大的人力和时间成本,才能完成此次bug的修复。
使用JSPatch可以解决这样的问题,只需在项目中引入JSPatch,就可以在发现bug时下发JS脚本补丁,替换原生方法,无需更新APP即时修复bug。
例子
|
01
02
03
04
05
06
07
08
09
10
|
@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:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
#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 |
|
01
02
03
04
05
06
07
08
09
10
11
12
|
//JSdefineClass("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一样,但不推荐这么做,因为:
- JSPatch和wax一样都是通过Objective-C Runtime的接口通过字符串反射找到对应的类和方法进行调用,这中间的字符串处理会损耗一定的性能,另外两种语言间的类型转换也有性能损耗,若用来做一个完整的业务模块,大量的频繁来回互调,可能有性能问题。
- 开发过程中需要用OC的思维写JS/Lua,丧失了脚本语言自己的特性。
- JSPatch和wax都没有IDE支持,开发效率低。
若想动态为APP添加模块,目前React Native给出了很好的方案,解决了上述三个问题:
- JS/OC不会频繁通信,会在事件触发时批量传递,提高效率。(详见React Native通信机制详解)
- 开发过程无需考虑OC的感受,遵从React框架的思想进行纯JS开发就行,剩下的事情React Native帮你处理好了。
- React Native连IDE都准备好了。
所以动态添加业务模块目前还是推荐尝试React Native,但React Native并不会提供原生OC接口的反射调用和方法替换,无法做到修改原生代码,JSPatch以小巧的引擎补足这个缺口,配合React Native用统一的JS语言让一个原生APP时刻处于可扩展可修改的状态。
目前JSPatch处于开发阶段,稳定性和功能还存在一些问题,欢迎大家提建议/bug/PR,一起来做这个项目。
——————
版权声明:本文章在微信公众平台的发表权,已「独家代理」给指定公众帐号:iOS开发(iOSDevTips)。
完全想到一个地方去了, 我上周在构思这个, 这几天在列提纲, 也打算写一篇类似的文章, 不过楼主更进一步, Demo 都已经做出来了.
楼主真乃大神也,JSPach真是神器,膜拜!
大神,看你的源码看不懂,架构功底好深,期待原理篇啊
JSPatch和越狱技术的cycript技术貌似类似,请问这个技术有什么关联?
大致看了下,方法调用的原理应该一样吧,没有方法替换
具体怎么 增量更新的呢 有空写个教程吧
请问个问题,jspatch和目前的wax+lua 相比有什么优势呢?
1.小巧
2.JS 语言
3.使用内置 JavaScriptCore 引擎,符合 Apple 审核规则
4.线程安全,替换的方法支持多线程调用
5.支持替换方法名包含下划线的方法
6.支持 Block / GCD
它的劣势是什么呢
文章里说了,不支持iOS6以及目前性能内存上不及lua
大神,github上下的demo,,用不了以下的方法,,
dataSource: function() {
// get the original method by adding prefix ‘ORIG’
var data = self.ORIGdataSource();
return data.push(‘Good!’);
}
找不到push的方法,,上面都改了些什么?
还有就是获取不了OC的成员变量,,,刚发的版本可以实现,搞不懂项目咋整。求解。。
@property(nonamotic, strong) NSArray *datas;
js:
self.datas(),应该这个是能调用,刚才下载最新版本的JSPatch都用不了,再次求解
新的版本从OC返回的NSString/NSArray/NSDictionary都是当成NSObject对象,不会自动转成JS类型了
这样可以调它们的OC方法:self.datas().objectAtIndex(0),如果要像之前那样转成JS类型,再调用下.toJS(),data.toJS().push(‘Good!’)
谢谢。
楼主,我想问下,假设为需要设置一个label的属性时,需要选择两种枚举型数据,咋整?
label.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
我在js文件下,设置如下:
label.setAutoresizingMask(2|5);
好像后面那个5(高度自适应)不起作用。。是不是在js中,,这里的枚举类型是怎么按位或呢?在线求解。。框架着实强大,学习中。
哦,我解决了,原来那个枚举型的值是16进制,,改成(2|16),就好了。
有问题可以加Q群聊哈 207283178
哎呀,有qq群早点说哇,,太好了。
目前支持导入分类么。?
[…] JSPatch – 动态更新iOS APP: http://blog.cnbang.net/works/2767/ […]
大神,项目里一部分模块用swift写的,但是比如tableview 什么的都是cocoa框架的东西,用这个JSPatch可以更改swift里的方法吗
初步实验 JSPatch 是可以更改 swift 里的方法,不过按 swift 的规则,defineClass 的类名要变成 ‘项目名.类名’
你用atom吗?用起来是卡的飞起吗
楼主,你试过用动态库实现热更新吗,最近有些问题
写的很好,谢谢分享
楼主的博文挺不错的!看了你的一片11年写的,我是四国军棋忠实粉丝,也打算用node和socket IO搭个在线得四国军棋。
如果你能像看别人缺点一样,如此准确的发现自己的缺点,那么你的生命将会不平凡。
JSPatch可以修复OC代码里面的C写的Foundation这类吗?只能修复OC吗?之前修复OC的代码没问题,就是遇到C的代码,不知道如何修复
macbook pro 2014 定制最高配 依旧卡的不行..
文档有写
楼主问个问题 A继承于B,A如何在js中获得B里面的属性
大神你好
首先感谢你的分享,写得很棒。其次问个问题,JSPatch 之前没用过,我在提交了项目以后,更改的JS文件能修复审核中的项目吗?
如果我有好多个BUG,是不是要写多个JS文件去调用
JS跟swift好相似啊
能,可以写在一个JS文件里
[…] 来源: JSPatch – 动态更新iOS APP […]
里面网络获取的url是自己定义的服务器么?更新js直接上传自己服务器js文件,应用自己下载js文件更新就可以了,是这个过程吗?
[…] 实例007:JSPatch – 动态更新iOS APP,实时修复线上版本的bug […]
[…] APP具備熱更新的能力,在實現 JSPatch […]
[…] 詳細介紹,可以看 @Bang 的這篇博文,連結在 這裡 […]
大神太牛了
T

浙公网安备 33010602011771号
沙发啊… 给大神占了