JSPatch是一個輕量的JS引擎,可以使用JavaScript語言來調用任何object-c接口,替換任何原生的方法。目前主要用於發步JS腳本替換原生Objective-C代碼,實時修復線上buggit
利用OC語言的動態性,動態的修改類的方法和屬性。在app啓動的時候加載咱們寫好的JavaScript文件並經過JavaScriptCore來執行,用JS寫好的類函數去篡改原有的OC函數。JSPatch只提供了篡改這個過程的代碼,像部署線上Js代碼、下載這些邏輯都得本身寫。固然你能夠用JSpatchSDK這個平臺,這個平臺幫咱們部署JS代碼、下載等一些邏輯。JSPatchSDK是收費的,也有免費版的。github
經過pod或者其餘方式引入JSPatch,在didFinishLaunchingWithOptions函數加上以下代碼:web
[JPEngine startEngine]; NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"js"]; NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil]; [JPEngine evaluateScript:script];
部署完OC代碼,咱們就能夠在index.js專心寫JavaScript來修復線上的bug.json
defineClass(classDeclaration, [properties,] instanceMethods, classMethods)
classDeclaration:字符串類型,表明類名字
properties:一個字符串數組,表明要添加的屬性列表
instanceMethods:實例方法
classMethods:累方法
[JPEngine startEngine]; NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"js"]; NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil]; [JPEngine evaluateScript:script]; self.window = [[UIWindow alloc]initWithFrame:[[UIScreen mainScreen] bounds]]; UINavigationController * navi = [[UINavigationController alloc]initWithRootViewController:[[MainViewController alloc]initWithNibName:@"MainViewController" bundle:nil]]; self.window.rootViewController = navi; [self.window makeKeyAndVisible];
// // MainViewController.h // JSPatchDemo // // Created by 朱國清 on 16/12/23. // Copyright © 2016年 bang. All rights reserved. // #import <UIKit/UIKit.h> @interface MainViewController : UIViewController @property (nonatomic,strong) NSArray * testArray; @property (nonatomic,strong) NSDictionary * testDictionary; @end
// // MainViewController.m // JSPatchDemo // // Created by 朱國清 on 16/12/23. // Copyright © 2016年 bang. All rights reserved. // #import "MainViewController.h" @interface MainViewController () @property NSString * privateKey; @end @implementation MainViewController - (void)viewDidLoad { [super viewDidLoad]; self.testArray = @[@"Apple",@"Boy",@"Cat",@"Dog",]; self.testDictionary = @{@"name":@"jack",@"age":@20}; self.privateKey = @"i am a private key"; [self initUI]; } // js 覆蓋 -(void)initUI{ if (false) { UIButton * btn = [[UIButton alloc]initWithFrame:CGRectMake(10,30, 100, 30)]; [btn setTitle:@"go" forState:UIControlStateNormal]; [btn setBackgroundColor:[UIColor brownColor]]; [btn addTarget:self action:@selector(handleButton) forControlEvents:UIControlEventTouchUpInside]; } } -(void)handleButton{ } -(void)testString:(NSString *)string{ NSLog(@"testString : %@",string); } - (void)testPointer:(NSError **)error { NSError *err = [[NSError alloc]initWithDomain:@"com.jspatch" code:42 userInfo:nil]; *error = err; } -(void)request:(void(^)(NSString *content, BOOL success))callback { callback(@"I'm content", YES); } typedef void (^JSBlock)(NSString *str); -(JSBlock)getBlock{ JSBlock block = ^(NSString *str) { NSLog(@"I'm %@",str); }; return block; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } @end
defineClass( 'MainViewController', { viewWillAppear:function(animated){ // 1.調用父類 self.super() self.super().viewWillAppear(animated); // 2.設置navigationBarHidden屬性值 .setNavigationBarHidden(true) self.navigationController().setNavigationBarHidden(true); }, initUI:function(){ // 3.oc的NSString對象,在JS裏面是Object // 4.privateKey 爲私有屬性 var privateKey = self.valueForKey("privateKey"); self.testString(privateKey); console.log('privateKey:'+ privateKey); // 5.OC NSArray var item1 = self.testArray().objectAtIndex(0); console.log('item1:'+item1.toString()); // 6.OC NSArray => JS array var jsArray = self.testArray().toJS(); console.log('jsArray[0]:'+jsArray[0]); // 7.OC NSDictionary var name1 = self.testDictionary().valueForKey('name'); console.log('name1:'+name1); // 8.OC NSDictionary => JS json var dic = self.testDictionary().toJS(); console.log('name2:'+dic['name']); // 10.獲取屬性 self.view() // 11.獲取一個類 require('UIColor') self.view().setBackgroundColor(require('UIColor').grayColor()); /* 12. 特殊變量 point {x:, y: } size {width:, height:} CGRect {x:, y:, width:, height:} range {location:, length:} */ var btn = require('UIButton').alloc().initWithFrame({x:20, y:30, width:100, height:30}); btn.setBackgroundColor(UIColor.brownColor()); // 13.多參數函數 [btn setTitle:forState:];以_代替: btn.setTitle_forState() btn.setTitle_forState('go',0); // 14.Selector 用字符代替 btn.addTarget_action_forControlEvents(self,'handlePress',1 << 6); self.view().addSubview(btn); // 15.block沒法使用self,須要保存一下 var slf = self; // 16. // 弱引用 var weakSelf = __weak(self) // 強引用 var strongSelf = __strong(self) // 17. block // block block的參數類型用字符串標示,block對象用NSBlock*類型 /* 從 JS 傳 block 到 OC,有兩個限制: A. block 參數個數最多支持6個。(若須要支持更多,能夠修改源碼) B. block 參數類型不能是 double / NSBlock / struct 類型。 */ self.request(block("NSString *, BOOL", function(ctn, succ) { if (succ) console.log(ctn) //output: I'm content slf.testString("block self"); })); // 18. oc 返回的block就是一個js函數,能夠直接調用 slf.getBlock()('JavaScrict'); // 19.GCD // JS dispatch_after(1.0, function(){ console.log('dispatch_after 1s'); }) dispatch_async_main(function(){ console.log('dispatch_async_main'); }) dispatch_sync_main(function(){ console.log('dispatch_sync_main'); }) dispatch_async_global_queue(function(){ console.log('dispatch_async_global_queue'); }) // 20.參入id * 指針 //malloc() pval() free() is provided by JPMemory extension require('JPEngine').addExtensions(['JPMemory']) var pError = malloc(sizeof("id")) self.testPointer(pError) var error = pval(pError) if (!error) { console.log("success") } else { console.log(error) } releaseTmpObj(pError) free(pError) }, handlePress:function(){ console.log('handlePress'); var vc = InfoViewController.alloc().init(); self.navigationController().pushViewController_animated(vc,true); } } ) // 21.定義一個繼承 UIViewController的類 // 22.定義兩個屬性 url、info defineClass('InfoViewController : UIViewController', [ 'url', 'info', ], { viewDidLoad:function(){ self.super().viewDidLoad(); self.navigationController().setNavigationBarHidden(false); self.view().setBackgroundColor(require('UIColor').whiteColor()); self.setUrl('www.pingan.com.cn'); console.log(self.url()); } } );