话说很久以前,iOS审核需要很久的时间,随随便便一周起,所以紧急的bug修复,时间紧迫的功能更新等都会受到审核时间的影响,iOSer默默承受了许多无端的指责,众人渴望能够出现一个可以让我们在需要的时候随时可以进行应用更新,而不用再受限制于各种各样的审核规则,于是大神bang的JSPatch横空出世,润泽了无数双充满期待的眼神.这篇文章我们先简单聊聊如何JSPatch如何使用.以下使用场景基于JSPatch 1.1.3,且假定在应用在收到下发的js之后可以立即执行.如果你还不了解基本使用,可以在这里点击查看.
1.1 紧急bug修复
1.1.1 替换掉原始的方法实现
假如你不小心脑袋一热写下了如下代码:
/** 监测到内存报警时移除当前不在window上的视图 */ - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; if (!self.view.window) { self.view = nil; } }
然后你发现应用内存几乎经常受到警告,然后界面一切漆黑.这时候你只需要偷偷找个时机,悄悄将下属代码下发给应用
defineClass("ViewController", { didReceiveMemoryWarning: function(){ self.super(). didReceiveMemoryWarning(); } })
这样我们就把原始的的实现给覆盖掉了.
再假如,虽然你已经很小心了,但是那些个异常判断还是不小心遗漏了一个:
- (void)viewDidLoad { [super viewDidLoad]; [self loadDataWithResult:^(NSString *message) { if (message.length) { self.label.text = message; } }]; // Do any additional setup after loading the view. }
然后突然message变成了NSNull的对象,该对象没有length方法,然后又跪了.这时候可以
defineClass("viewController", { viewDidLoad: function() { self.super().viewDidLoad(); self.loadDataWithResult(block("void, NSString *", function(message){ if (typeof(message) == 'undefined') { self.label().setText("不好意思,数据迷路了...") } }) } });
再来一段js拯救一下:
defineClass("viewController", { viewDidLoad: function() { self.super().viewDidLoad(); self.loadDataWithResult(block("void, NSString *", function(message){ if (typeof(message) == 'undefined') { self.label().setText("不好意思,数据迷路了...") } }) } });
1.2 在原有方法上增加新的实现
产品想要在某个界面右上角添加一个圆形的红色按钮,点击之后弹出一个弹窗展示提示信息 :
require('UIAlertController', 'UIButton', 'UIScreen', 'UIColor', 'UIAlertAction') console.log() defineClass('ViewController', { viewDidLoad: function() { //在方法前添加ORIG可以调用原来的实现 self.super().ORIGviewDidLoad(); var width = UIScreen.mainScreen().bounds().width; var btn = UIButton.alloc().initWithFrame({x:width - 60, y:40, width: 40, height:40}); btn.layer().setCornerRadius(20.0); btn.addTarget_action_forControlEvents(self, "btnClick", 1 << 6); btn.setBackgroundColor(UIColor.redColor()); self.view().addSubview(btn); }, btnClick:function() { var alerController = UIAlertController.alertControllerWithTitle_message_preferredStyle("UIAlertController", "You haved Clicked!", 1); var action = UIAlertAction.actionWithTitle_style_handler("OK", 1, block("void, UIAlertAction *", function(ac){})); alerController.addAction(action); self.presentViewController_animated_completion(alerController, true, block("void, void", function(){})); } })
1.3 增加新的功能模块
虽然理论上JS基本上可以解决几乎所有的需求,但事实上,书写大量的JS代码对一个一个原生开发者来说体验并不那么舒适,所以应该JSPach主要还是用在情不得已的热修复上,并不合适书写大的功能模块,因为在新的版本可以发布时应该及时将JSPatch热修复的问题转化成原生实现进行替换,同时撤销JS在该版本上的下发执行.那么现在然我们来基于1.2 的按钮点击事件来弹出一个新的控制器界面.
require('UIButton', 'UIScreen', 'UIColor','DSTableViewController', 'NSDate', 'UIAlertController', 'UIAlertAction') console.log() defineClass('ViewController', { viewDidLoad: function() { self.super().viewDidLoad(); var width = UIScreen.mainScreen().bounds().width; var btn = UIButton.alloc().initWithFrame({x:width - 60, y:40, width: 40, height:40}); btn.layer().setCornerRadius(20.0); btn.addTarget_action_forControlEvents(self, "btnClick", 1 << 6); btn.setBackgroundColor(UIColor.redColor()); self.view().addSubview(btn); }, btnClick:function() { var tc = DSTableViewController.alloc().init(); self.presentViewController_animated_completion(tc, true, block("void, void", function(){})); } }) defineClass('DSTableViewController : UITableViewController <UIAlertViewDelegate>', ['data'], { dataSource: function() { var data = self.data(); if (data) return data; var data = []; for (var i = 0; i < 20; i ++) { data.push(Date().toString()); } self.setData(data) return data; }, numberOfSectionsInTableView: function(tableView) { return 1; }, tableView_numberOfRowsInSection: function(tableView, section) { return self.dataSource().length; }, 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()[indexPath.row()]) return cell }, tableView_heightForRowAtIndexPath: function(tableView, indexPath) { return 60 }, tableView_didSelectRowAtIndexPath: function(tableView, indexPath) { tableView.deselectRowAtIndexPath_animated(indexPath, true); var alerController = UIAlertController.alertControllerWithTitle_message_preferredStyle("UIAlertController", "You haved Clicked!", 1); var action = UIAlertAction.actionWithTitle_style_handler("OK", 1, block("void, UIAlertAction *", function(ac){})); alerController.addAction(action); self.presentViewController_animated_completion(alerController, true, block("void, void", function(){})); }, alertView_willDismissWithButtonIndex: function(alertView, idx) { console.log('click btn ' + alertView.buttonTitleAtIndex(idx).toJS()) }, })