iOS 热加载之 JSPatch
来自: https://testerhome.com/topics/4084
起因
最近接了一些任务,为了将来应用更快的热更新做准备,所以就查了一些文档。查到了这个工具,在一阵恶心之后,大概略知一二了。其实感觉和之前这篇差不多,但又差别很大。没有看过的同学可以先看lua in iOS
JSPatch
JSPatch
是热加载的方案,应该都是base在iOS上面的。我们可以在我们的pod文件中增加
platform :ios, '6.0' pod 'JSPatch'
以引入这个模块。
JSPatch
在今天了解一下之后可以说是在OC和JS之间做了一个桥梁,并且适应性很好,在语法对应上也做的很好。但一般这种热加载的框架并不是那么万能。从JSPatch
的定位和我自己用下来的感受来看,这个框架的确是为了修复一些bug而存在的,并不是真正的为了热加载或者说专门为了应用热加载存在的,至少我试下来目前局限性还是蛮大的。比如纯粹的UIView
适应性还很强,但其他各个控件的所有方法就不是全部兼容了,大家往下看吧。
AppDelegate.h
我们先来看官方文档上面的demo吧。首先先来看AppDelegate.m
#import "AppDelegate.h" #import "JPEngine.h" #import "JPViewController.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [JPEngine startEngine]; NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"monkey" ofType:@"js"]; NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil]; [JPEngine evaluateScript:script]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; JPViewController *rootViewController = [[JPViewController alloc] init]; UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController]; self.window.rootViewController = navigationController; [self.window makeKeyAndVisible]; return YES; } @end
这里和我们一般的iOS应用一样,但有区别的是在复写didFinishLaunchingWithOptions
方法之后就启动了JPEngine
,也就是我们的JSPatch
,接下来就是定义我们的文件名,我们的文件后缀,以及一系列界面的初始化。
JPViewController.h
接续来看这个界面的具体实现:
#import "JPViewController.h" @implementation JPViewController - (void)viewDidLoad { [super viewDidLoad]; UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 100, [UIScreen mainScreen].bounds.size.width, 50)]; [btn setTitle:@"Push JPTableViewController" forState:UIControlStateNormal]; [btn addTarget:self action:@user4(handleBtn:) forControlEvents:UIControlEventTouchUpInside]; [btn setBackgroundColor:[UIColor grayColor]]; [self.view addSubview:btn]; } - (void)handleBtn:(id)sender { } @end
这里不难理解。在界面上放了一个按钮,定义了按钮一系列的属性。好了接下来就是关键了,一般在storyboard
里面会直接去让ide自动生成按钮对应的sender方法,这里是定义了一个空方法。其实就很容易想到这个方法的实现必然就在JS里面了。
JS初始化类和方法
接着我们来看比较重要的js了。
defineClass('JPViewController', { handleBtn: function(sender) { var tableViewCtrl = JPTableViewController.alloc().init() self.navigationController().pushViewController_animated(tableViewCtrl, YES) } })
首先JSPatch
允许我们直接在js里面去定义新方法或者重新定义OC下面定义的类。比如这里我们重新定义了之后,在里面实现了我们的点击方法并实现了一个TableView
。
defineClass('JPTableViewController : UITableViewController <UIAlertViewDelegate>', { dataSource: function() { var data = self.getProp('data') if (data) return data; var data = []; for (var i = 0; i < 20; i ++) { data.push("monkey test " + i); } self.setProp_forKey(data, 'data') return data; },
这里就开始进一步实现了,首先是table
里面的数据。
JS重载OC方法
在JSPatch
里面会有一套很好的方法对应关系,同样的方法,OC中如果是空格,JS就是,OC中如果是,那么JS中就是__。基本上就是这样一个对应关系。我们来看下。
- OC
//返回有多少个Sections - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; }
- JS
numberOfSectionsInTableView: function(tableView) { return 1 ; },
- OC
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section;
- JS
tableView_numberOfRowsInSection: function(tableView, section) { return self.dataSource().count(); },
- OC
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
- JS
tableView_cellForRowAtIndexPath: function(tableView, indexPath) { var cell = tableView.dequeueReusableCellWithIdentifier("cell") if (!cell) { cell = require('UITableViewCell').alloc().initWithStyle_reuseIdentifier(0, "cell") } cell.textLabel().setText(self.dataSource().objectAtIndex(indexPath.row())) return cell },
如果想要调用OC原生组件的方法,可以先在JS最头定义require
就可以了。
DEMO效果
点击按钮进去之后的就是JS实现的界面和交互了。
其实也可以直接重载ViewDidLoad
,这样的话就可以直接去实现开始的界面。我们可以看下另外一个实现的界面,就会好很多。
require('UIColor,UIView,UILabel,UIFont,UIImageView,UIImage');