iOS 热加载之 JSPatch

zihuang12 9年前

来自: 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');