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