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'); 