JSPatch是最近业余作的项目,只需在项目中引入极小的引擎,就可使用JavaScript调用任何Objective-C的原生接口,得到脚本语言的能力:动态更新APP,替换项目原生代码修复bug。前端
用途
是否有过这样的经历:新版本上线后发现有个严重的bug,可能会致使crash率激增,可能会使网络请求没法发出,这时能作的只是赶忙修复bug而后提交等待漫长的AppStore审核,再盼望用户快点升级,付出巨大的人力和时间成本,才能完成这次bug的修复。ios
使用JSPatch能够解决这样的问题,只需在项目中引入JSPatch,就能够在发现bug时下发JS脚本补丁,替换原生方法,无需更新APP即时修复bug。git
例子
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:github
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
|
//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。web
更多的使用文档和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)。