iOS中使用JSPatch架構使Objective-C與JavaScript代碼互動_IOS

來源:互聯網
上載者:User

JSPatch是GitHub上一個開源的架構,其可以通過Objective-C的run-time機制動態使用JavaScript調用與替換項目中的Objective-C屬性與方法。其架構小巧,代碼簡潔,並且通過系統的JavaScriptCore架構與Objective-C進行互動,這使其在安全性和審核風險上都有很強的優勢。Git源碼地址:https://github.com/bang590/JSPatch。

一、從一個官方的小demo看起

通過cocoapods將JSPath整合進一個Xcode工程中,在AppDelegate類的中編寫如下代碼:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {  //開始初始化引擎  [JPEngine startEngine];  //讀取js檔案  NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];  NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil];  //運行js檔案  [JPEngine evaluateScript:script];  self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];  self.window.rootViewController = [[ViewController alloc]init];  [self.window addSubview:[self genView]];  [self.window makeKeyAndVisible];  return YES;}- (UIView *)genView{  UIView * view= [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 320)];  view.backgroundColor = [UIColor redColor];  return view;}

在工程中添加一個js檔案,編寫如下:

  

 require('UIView, UIColor, UILabel')  //要替換函數的類  defineClass('AppDelegate', {      //替換函數        //要替換函數的名稱        genView: function() {          var view = self.ORIGgenView();          view.setBackgroundColor(UIColor.greenColor())          var label = UILabel.alloc().initWithFrame(view.frame());          label.setText("JSPatch");          label.setTextAlignment(1);          view.addSubview(label);          return view;      }  });

運行工程,可以看到genView方法被替換成了js檔案中的方法,原本紅色的視圖被修改成了綠色。

二、使用JavaScript代碼向Objective-C中修改或添加方法

JSPatch引擎中支援3中方式進行JavaScript代碼的調用,分別是使用JavaScript字串進行代碼運行,讀取本地的JavaScript檔案進行代碼運行和擷取網路的JavaScript檔案進行代碼運行。例如,如果想要通過JavaScript代碼在項目中彈出一個警告框,在Objective-C代碼中插入如下代碼:

- (void)viewDidLoad {  [super viewDidLoad];  // ‘\'符用於進行換行  [JPEngine evaluateScript:@"\   var alertView = require('UIAlertView').alloc().init();\   alertView.setTitle('Alert');\   alertView.setMessage('AlertView from js'); \   alertView.addButtonWithTitle('OK');\   alertView.show(); \   "];}

開發人員也可以動態在Objective-C類檔案中添加方法,例如在ViewController類中編寫如下:

- (void)viewDidLoad {  [super viewDidLoad];  self.view.backgroundColor = [UIColor whiteColor];  [JPEngine startEngine];  NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];  NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil];  [JPEngine evaluateScript:script];  [self performSelectorOnMainThread:@selector(creatView) withObject:nil waitUntilDone:nil];}

JavaScript檔案代碼如下:

 require('UIView, UIColor, UILabel')  defineClass('ViewController', {      // replace the -genView method        creatView: function() {          var view = UIView.alloc().initWithFrame({x:20, y:20, width:100, height:100});          view.setBackgroundColor(UIColor.greenColor());          var label = UILabel.alloc().initWithFrame({x:0, y:0, width:100, height:100});          label.setText("JSPatch");          label.setTextAlignment(1);          view.addSubview(label);        self.view().addSubview(view)      }  });

除了上面的代碼,在ViewController.m檔案中沒有編寫任何其他的方法,運行工程,可以看到程式並沒有崩潰,ViewController執行了creatView方法。

通過上面的樣本,我們發現使用JSPatch可以做一些十分有趣的事。對於iOS應用來說,通過官方渠道AppStore進行應用程式的發布要通過人工審核,有時這個審核周期會非常長,如果在開發人員在編寫代碼時留下了一些小漏洞,應用一旦上線,若要修改掉這個bug就十分艱難了。有了JSPatch,我們可以想象,如果可以定位到線上應用有問題的方法,使用JS檔案來修改掉這個方法,這將是多麼cool的一件事,事實上,JSPatch的主要用途也是可以實現線上應用極小問題的hotfix。

三、JavaScript與Objective-C互動的基礎方法

要使用JSPatch來進行Objective-C風格的方法編寫,需要遵守一些JavaScript與Objective-C互動的規則。

1.在JavaScript檔案中使用Objective-C類

在編寫JavaScript代碼時如果需要用到Objective-C的類,必須先對這個類進行require引用,例如,如果需要使用UIView這個類,需要在使用前進行如下引用:

require('UIView')

同樣也可以一次對多個Objective-C類進行引用:

require('UIView, UIColor, UILabel')

還有一種更加簡便的寫法,直接在使用的時候對其進行引用:

require('UIView').alloc().init()

2.在JavaScript檔案中進行Objective-C方法的調用

在進行Objective-C方法的調用時,分為兩種,一種是調用類方法,一種是調用類的對象方法。

調用類方法:通過類名打點的方式來調用類方法,格式類似如下,括弧內為參數傳遞:

UIColor.redColor()

調用執行個體方法:通過對象打點的方式調用類的執行個體方法,格式如下,括弧內為參數傳遞:

view.addSubview(label)

對於Objective-C中的多參數方法,轉化為JavaScript將參數分割的位置以_進行分割,參數全部放入後面的括弧中,以逗號分割,樣本如下:

view.setBackgroundColor(UIColor.colorWithRed_green_blue_alpha(0,0.5,0.5,1))
對於Objective-C類的屬性變數,在JavaScript中只能使用getter與setter方法來訪問,樣本如下:

label.setText("JSPatch")

提示:如果原Objective-C的方法中已經包含了_符號,則在JavaScript中使用__代替。

3.在JavaScript中操作與修改Objective-C類

JSPatch的最大應用是在應用運行時動態操作和修改類。

重寫或者添加類的方法:

在JavaScript中使用defineClass來定義和修改類中的方法,其編寫格式如下所示:

/*classDeclaration:要添加或者重寫方法的類名 字串 如果此類不存在 則會建立新的類instanceMethods:要添加或者重寫的執行個體方法 {}classMethods:要添加或者重寫的類方法 {}*/defineClass(classDeclaration, instanceMethods, classMethods)

樣本如下:

defineClass('ViewController', {      // replace the -genView method        newFunc: function() {          //編寫執行個體方法          self.view().setBackgroundColor(UIColor.redColor())        }        },{        myLoad:function(){          //編寫類方法        }      }      )

如果在重寫了類中的方法後要調用原方法,需要使用ORIG首碼,樣本如下:

defineClass('ViewController', {      // replace the -genView method        viewDidLoad: function() {          //編寫執行個體方法          self.ORIGviewDidLoad()        }        }      )

對於Objective-C中super關鍵字調用的方法,在JavaScript中可以使用self.super()來調用,例如:

defineClass('ViewController', {      // replace the -genView method        viewDidLoad: function() {          //編寫執行個體方法          self.super().viewDidLoad()        }        }      )

同樣JSPatch也可以為類添加臨時屬性,用於在方法間參數傳遞,使用set_Prop_forKey()來添加屬性,使用getProp()來擷取屬性,注意,JSPatch添加的屬性不能使用Objective-C的setter與getter方法訪問,如下:

defineClass('ViewController', {      // replace the -genView method        viewDidLoad: function() {          //編寫執行個體方法          self.super().viewDidLoad()          self.setProp_forKey("JSPatch", "data")        },        touchesBegan_withEvent(id,touch){          self.getProp("data")          self.view().setBackgroundColor(UIColor.redColor())        }        }      )

關於為類添加協議的遵守,和Objective-C中遵守協議的方式一致,如下:

defineClass("ViewController2: UIViewController <UIAlertViewDelegate>", {      viewDidAppear: function(animated) {      var alertView = require('UIAlertView')      .alloc()      .initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles(                                        "Alert",                                        "content",                                        self,                                        "OK",                                        null                                        )      alertView.show()      },      alertView_clickedButtonAtIndex:function(alertView, buttonIndex) {      console.log('clicked index ' + buttonIndex)      }      })

四、JavaScript與Objective-C互動的幾種常用類型

1.結構體

在Objective-C代碼中,我們經常會使用到結構體,JSPatch中原生支援的結構體有如下幾種:CGPoint,CGSize,CGRect,NSRange。並且這幾種結構體在進行介面操作時也會經常使用到。

對於CGRect類型,JavaScript使用如下代碼建立:

  var view = require('UIView').alloc().init()  view.setFrame({x:100,y:100,width:100,height:100})

對於CGPoint類型,JavaScript使用如下代碼建立:

   

view.setCenter({x:200,y:200})

對於CGSize類型,JavaScript使用如下代碼建立:

  var size = {width:200,height:200}  view.setFrame({x:100,y:100,width:size.width,height:size.height})

對於NSRange類型,JavaScript使用如下代碼建立:

  var range = {location: 0, length: 1}

2.選取器Selector

對於Objective-C中的方法選取器Selector,在JavaScript中使用字串的形式建立,例如:

self.performSelector_withObject("func:", 1)

3.關於Null 物件

在JavaScript中,null與undefined都對應於Objective-C中的nil,Objective-C中的NSNullNull 物件,在JavaScript中使用nsnull來代替。

4.在Objective-C與JavaScript中進行block的互動

在JavaScript與Objective-C進行block互動有兩種方式,一種是在JavaScript檔案中調用Objective-C中的block,一種是將JavaScript檔案中的函數塊作為block參數傳遞給Objective-C。

在JavaScript檔案中使用Objective-C中的block十分簡單,因為JavaScript中沒有block的概念,Objective-C會被自動轉換為函數,樣本如下:

Objective-C:

typedef void(^block)(NSString * str);@interface ViewController ()@end@implementation ViewController-(block)getBlock{  block block = ^(NSString * str){NSLog(@"%@",str);};  return block;}@end

JavaScript:

defineClass("ViewController", {      viewDidAppear: function(animated) {       var func = self.getBlock()        func("123")      }      })

在JavaScript檔案中將func作為參數block傳遞給Objective-C就複雜一些,需要使用block()方法進行封裝,例如:

Objective-C:

@interface ViewController ()@end@implementation ViewController-(void)run:(void(^)(NSString * str))block{  block(@"123");}@end

JavaScript:

defineClass("ViewController", {      viewDidAppear: function(animated) {      //run 方法中需要傳入一個block      self.run(block("NSString*",function(str){console.log(str)}))      }      })

在使用block()方法對JavaScript中的Func進行封裝時,block(param1,param2)有兩個參數,第1個參數設定func中的參數類型,如果有多個參數,使用逗號分割;第2個參數為func函數體。

注意:在block()封裝的func中不可以使用self指標,如果需要使用self,需要在block外進行臨時變數的轉換,樣本如下:

defineClass("ViewController", {      viewDidAppear: function(animated) {      //run 方法中需要傳入一個block      var slf = self      self.run(block("NSString*",              function(str){              console.log(str)              slf.log(str)              }))      }      })

在JavaScript中分別使用__weak()與__strong來聲明弱引用與強引用對象,例如:

 var slf = __weak(self) var stgSef = __strong(self)

5.關於GCD與枚舉

在JSPatch中,可以使用如下JavaScript代碼來調用GCD方法:

//阻塞當前線程一定時間dispatch_after(1.0, function(){ })//為主線程添加非同步任務dispatch_async_main(function(){ })//為主線程添加同步任務dispatch_sync_main(function(){ })//向全域隊列中新增工作dispatch_async_global_queue(function(){ })JSPatch中不可以直接使用Objective-C中定義的枚舉,但是可以用其枚舉的真實值進行傳遞。例如://UIControlEventTouchUpInside的值是1<<6btn.addTarget_action_forControlEvents(self, "handleBtn", 1<<6);

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.