oc與js 以及第三方 Js bridge

寫之前先給大家介紹一款好用的第三方 DSBridge-IOS:https://github.com/wendux/DSBridge-IOS

DSBridge-Android:https://github.com/wendux/DSBridge-Android

與WebViewJavascriptBridge的對比請移步DSBridge VS WebViewJavascriptBridge

DSBridge名稱的由來:DSBridge是DSpider項目使用的 javascript bridge, 分離自

五分鐘了解DSBridge

1.了解js


前陣子,Apple正式發布了新的iOS 7系統,最大最直觀的改變在于界面變得小清新范了,我也提到《iOS,你真的越來越像Android了》。不過對于移動開發者來說,除了要適應Xcode 5,最應該關注的還是iOS 7在開發接口的上的變化。概覽Apple提供的官方文檔《What’s New in iOS》,最最讓我欣喜的是iOS 7中加入了JavaScriptCore框架。該框架讓Objective-C和JavaScript代碼直接的交互變得更加的簡單方便。JavaScriptCore這個框架其實只是基于webkit中以C/C++實現的JavaScriptCore的一個包裝,在舊版本iOS開發中,很多開發者也會自行將webkit的庫引入項目編譯使用。不過雖然iOS7把它當成了標準庫,可惜目前,我還沒有在Apple Developer中找到像之前文章中收集的那樣的官方文檔介紹這個框架的具體使用方法。好在還可以在Xcode中找到頭文件,而且里面的注釋對每個類和方法的功能寫得還算清楚,再結合網上僅有的幾篇文章的介紹,我也在此簡單入門一下JavaScriptCore。更多細節還待開發過程中發現。JavaScriptCore中的類JavaScriptCore_Head在項目中引入JavaScriptCore后,鏈到頭文件中,除了大段的Copyright注釋可以看到里面只要引入了5個文件,每個文件里都定義跟文件名對應的類:JSContextJSValueJSManagedValueJSVirtualMachineJSExport雖說代碼中的注釋介紹的也比較詳細了,但是如同一圖頂萬言,對程序員來說代碼更有說服力。本文就先來說說這些類相對比較好理解但又很常用的JSContext和JSValue以及它們方法的使用方式和效果。JSContext和JSValueJSVirtualMachine為JavaScript的運行提供了底層資源,JSContext就為其提供著運行環境,通過- (JSValue *)evaluateScript:(NSString *)script;方法就可以執行一段JavaScript腳本,并且如果其中有方法、變量等信息都會被存儲在其中以便在需要的時候使用。而JSContext的創建都是基于JSVirtualMachine:- (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine;,如果是使用- (id)init;進行初始化,那么在其內部會自動創建一個新的JSVirtualMachine對象然后調用前邊的初始化方法。JSValue則可以說是JavaScript和Object-C之間互換的橋梁,它提供了多種方法可以方便地把JavaScript數據類型轉換成Objective-C,或者是轉換過去。其一一對應方式可見下表:OBJECTIVE-CJAVASCRIPTJSVALUE CONVERTJSVALUE CONSTRUCTORnilundefinedvalueWithUndefinedInContextNSNullnullvalueWithNullInContext:NSStringstringtoStringNSNumbernumber, booleantoNumbertoBooltoDoubletoInt32toUInt32valueWithBool:inContext:valueWithDouble:inContext:valueWithInt32:inContext:valueWithUInt32:inContext:NSDictionaryObject objecttoDictionaryvalueWithNewObjectInContext:NSArrayArray objecttoArrayvalueWithNewArrayInContext:NSDateDate objecttoDateNSBlockFunction objectidWrapper objecttoObjecttoObjectOfClass:valueWithObject:inContext:ClassConstructor object 基本類型轉換先看個簡單的例子:JSContext *context = [[JSContext alloc] init];JSValue *jsVal = [context evaluateScript:@"21+7"];int iVal = [jsVal toInt32];NSLog(@"JSValue: %@, int: %d", jsVal, iVal); //Output://? JSValue: 28, int: 28 很簡單吧,還可以存一個JavaScript變量在JSContext中,然后通過下標來獲取出來。而對于Array或者Object類型,JSValue也可以通過下標直接取值和賦值。JSContext *context = [[JSContext alloc] init];[context evaluateScript:@"var arr = [21, 7 , 'iderzheng.com'];"];JSValue *jsArr = context[@"arr"]; // Get array from JSContext NSLog(@"JS Array: %@;? ? Length: %@", jsArr, jsArr[@"length"]);jsArr[1] = @"blog"; // Use JSValue as arrayjsArr[7] = @7; NSLog(@"JS Array: %@;? ? Length: %d", jsArr, [jsArr[@"length"] toInt32]); NSArray *nsArr = [jsArr toArray];NSLog(@"NSArray: %@", nsArr); //Output://? JS Array: 21,7,iderzheng.com? ? Length: 3//? JS Array: 21,blog,iderzheng.com,,,,,7? ? Length: 8//? NSArray: (//? 21,//? blog,//? "iderzheng.com",//? "",//? "",//? "",//? "",

//? 7

//? )

通過輸出結果很容易看出代碼成功把數據從Objective-C賦到了JavaScript數組上,而且JSValue是遵循JavaScript的數組特性:無下標越位,自動延展數組大小。并且通過JSValue還可以獲取JavaScript對象上的屬性,比如例子中通過"length"就獲取到了JavaScript數組的長度。在轉成NSArray的時候,所有的信息也都正確轉換了過去。

方法的轉換

各種數據類型可以轉換,Objective-C的Block也可以傳入JSContext中當做JavaScript的方法使用。比如在前端開發中常用的log方法,雖然JavaScritpCore沒有自帶(畢竟不是在網頁上運行的,自然不會有window、document、console這些類了),仍然可以定義一個Block方法來調用NSLog來模擬:

JSContext *context = [[JSContext alloc] init];

context[@"log"] = ^() {

NSLog(@"+++++++Begin Log+++++++");

NSArray *args = [JSContext currentArguments];

for (JSValue *jsVal in args) {

NSLog(@"%@", jsVal);

}

JSValue *this = [JSContext currentThis];

NSLog(@"this: %@",this);

NSLog(@"-------End Log-------");

};

[context evaluateScript:@"log('ider', [7, 21], { hello:'world', js:100 });"];

//Output:

//? +++++++Begin Log+++++++

//? ider

//? 7,21

//? [object Object]

//? this: [object GlobalObject]

//? -------End Log-------

通過Block成功的在JavaScript調用方法回到了Objective-C,而且依然遵循JavaScript方法的各種特點,比如方法參數不固定。也因為這樣,JSContext提供了類方法來獲取參數列表(+ (NSArray *)currentArguments)和當前調用該方法的對象(+ (JSValue *)currentThis)。上邊的例子中對于"this"輸出的內容是GlobalObject,這也是JSContext對象方法- (JSValue *)globalObject;所返回的內容。因為我們知道在JavaScript里,所有全局變量和方法其實都是一個全局變量的屬性,在瀏覽器中是window,在JavaScriptCore是什么就不得而知了。

Block可以傳入JSContext作方法,但是JSValue沒有toBlock方法來把JavaScript方法變成Block在Objetive-C中使用。畢竟Block的參數個數和類型已經返回類型都是固定的。雖然不能把方法提取出來,但是JSValue提供了- (JSValue *)callWithArguments:(NSArray *)arguments;方法可以反過來將參數傳進去來調用方法。

JSContext *context = [[JSContext alloc] init];

[context evaluateScript:@"function add(a, b) { return a + b; }"];

JSValue *add = context[@"add"];

NSLog(@"Func:? %@", add);

JSValue *sum = [add callWithArguments:@[@(7), @(21)]];

NSLog(@"Sum:? %d",[sum toInt32]);

//OutPut:

//? Func:? function add(a, b) { return a + b; }

//? Sum:? 28

JSValue還提供- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments;讓我們可以直接簡單地調用對象上的方法。只是如果定義的方法是全局函數,那么很顯然應該在JSContext的globalObject對象上調用該方法;如果是某JavaScript對象上的方法,就應該用相應的JSValue對象調用。

異常處理

Objective-C的異常會在運行時被Xcode捕獲,而在JSContext中執行的JavaScript如果出現異常,只會被JSContext捕獲并存儲在exception屬性上,而不會向外拋出。時時刻刻檢查JSContext對象的exception是否不為nil顯然是不合適,更合理的方式是給JSContext對象設置exceptionHandler,它接受的是^(JSContext *context, JSValue *exceptionValue)形式的Block。其默認值就是將傳入的exceptionValue賦給傳入的context的exception屬性:

^(JSContext *context, JSValue *exceptionValue) {

context.exception = exceptionValue;

};

我們也可以給exceptionHandler賦予新的Block以便在JavaScript運行發生異常的時候我們可以立即知道:

JSContext *context = [[JSContext alloc] init];

context.exceptionHandler = ^(JSContext *con, JSValue *exception) {

NSLog(@"%@", exception);

con.exception = exception;

};

[context evaluateScript:@"ider.zheng = 21"];

//Output:

//? ReferenceError: Can't find variable: ider

使用Block的注意事項

從之前的例子和介紹應該有體會到Block在JavaScriptCore中起到的強大作用,它在JavaScript和Objective-C之間的轉換 建立起更多的橋梁,讓互通更方便。但是要注意的是無論是把Block傳給JSContext對象讓其變成JavaScript方法,還是把它賦給exceptionHandler屬性,在Block內都不要直接使用其外部定義的JSContext對象或者JSValue,應該將其當做參數傳入到Block中,或者通過JSContext的類方法+ (JSContext *)currentContext;來獲得。否則會造成循環引用使得內存無法被正確釋放。

比如上邊自定義異常處理方法,就是賦給傳入JSContext對象con,而不是其外創建的context對象,雖然它們其實是同一個對象。這是因為Block會對內部使用的在外部定義創建的對象做強引用,而JSContext也會對被賦予的Block做強引用,這樣它們之間就形成了循環引用(Circular Reference)使得內存無法正常釋放。

對于JSValue也不能直接從外部引用到Block中,因為每個JSValue上都有JSContext的引用 (@property(readonly, retain) JSContext *context;),JSContext再引用Block同樣也會形成引用循環。

2.常見應用?


注:此文只現在只推薦需要適配iOS7的同學讀,如果已經扔掉iOS7,強烈建議換用WKWebView。已出WKWebView文章WKWebView使用及注意點(keng)

functioncallCamera(){window.location.href ='toyun://callCamera';? ? }

test.html中的代碼解釋

這段代碼相比上面的那段測試代碼是很簡單的,同樣有一個按鈕,名字為CallCamera點擊之后調用自己的callCamera方法,window.location.href這里是改變主窗口的指向從而馬上發出一個鏈接為toyun://callCamera請求,而想要傳給原生應用的參數也可已包含到此請求中,而在iOS方法中我們要攔截這個請求,根據請求內容去判斷JavaScript想要做的事情,從而實現web頁面和本地應用之間的交互。

iOS

iOS對應的代碼

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType{NSString*url = request.URL.absoluteString;if([url rangeOfString:@"toyun://"].location !=NSNotFound) {// url的協議頭是toyunNSLog(@"callCamera");returnNO;? ? }returnYES;}

iOS對應的代碼的解釋

在webView的代理方法中去攔截自定義的協議Toyun://如果是此協議則據此判斷JavaScript想要做的事情,調用原生應用的方法,這些都是提前約定好的,同時阻止此鏈接的跳轉。



文章出處:

http://blog.iderzheng.com/introduction-to-ios7-javascriptcore-framework/

http://www.lxweimin.com/p/f896d73c670a

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,615評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,606評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,826評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,227評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,447評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,992評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,807評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,001評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,243評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,709評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,996評論 2 374

推薦閱讀更多精彩內容