咱們都知道WebKit是個渲染引擎,簡單來講負責頁面的佈局,繪製以及層的合成,可是WebKit工程中不單單有關於渲染相關的邏輯,也集成了默認的javascript引擎--JavaScriptCore,目前Safari的js引擎也基於JSC構建,不過有一些私有的優化,整體性能相差不大。JSC的執行理念比較符合傳統的引擎邏輯,它包括了2部分:解釋器和簡單方法JIT
。解釋器比較容易理解,針對某種類型的文件解釋執行,在JSC中,它的目標文件是由代碼構建的語法樹生成的字節碼文件,相似於java中的字節碼,不過在JSC中字節碼的執行是在基於寄存器的虛擬機中而不是基於棧,好處在於能夠方便的在ARM架構處理器中使用三地址指令,減小了次數較多的出棧和入棧等指令分派以及耗時的內存IO;JIT在java虛擬機中應用比較多,針對執行較屢次的熱點方法進行編譯爲本地方法,執行效率更高,JSC中的JIT同理。
在iOS7中,咱們能夠引入JSC框架,這樣,咱們能夠oc層來操做js層代碼的執行。另外JSC暴露了許多C層面的接口,咱們也能夠在底層來構建自定義的js執行環境,操做執行js代碼,可控執行可擴展性更強。javascript
既然有了這麼給力的引擎,咱們在構建hybrid app時能夠使用JSC來代替cordova的webViewJavascriptBridge框架完成簡易的接口暴露,將來在oc層逐漸能夠將UI組件模塊化,並經過JSExport暴露接口,由js層負責調用相應模塊的初始化方法完成界面的hybrid化。
oc端初始化一個js執行上下文JSContext對象很容易, [[JSContext alloc] init]
便可,可是在hybrid app中,經過這種方式初始化JSContext與承載頁面的UIWebVIew並非同一個js環境,所以咱們須要獲取UIWebView對應的JSContext。可是apple官方並未提供相關的方法,不過這邊難不倒某些人,有些人發現,經過KVC的方式可獲取UIWebView對應的JSContext,方式以下[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]
。一旦獲取到對應的JSContext,咱們能夠作的就有不少了。java
// 獲取對應的JSContext JSContext *context=[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; // 設置JSContext的錯誤處理函數 [context setExceptionHandler:^(JSContext *context, JSValue *value) { NSLog(@"oc catches the exception: %@", value); }]; // 組件化某個功能類或UIController ShowjoyFad *sf=[ShowjoyFad new]; // 暴露改類至JSContext中,在js層的全局屬性中咱們能夠訪問該類,如window.showjoyFad context[@"showjoyFad"]=sf; context[@"ViewController"] = self; // 引用js層定義的函數 JSValue * abc = context[@"abc"]; // 執行 JSValue * ret = [abc callWithArguments:@[@"helloworld"]]; NSLog(@"ret: %@",[ret toString]);
經過簡單的例子能夠很明顯的看出JSC通訊的簡潔性,與android的WebView通訊相似,native端能夠直接講接口注入到js上下文中,js在什麼時候的時機調用函數。可是這裏涉及到一個比較棘手的問題,JSContext的獲取實在UIWebView的那個階段呢?
我作過一個測試:首先在UIWebView的webViewDidStartLoad階段建立JSContext並暴露oc端的方法,在加載一級頁面時js正常調用oc的方法,而跳轉到二級頁面中卻沒法執行oc的方法;而在webViewDidStartLoad階段因爲並未加載完js文件, 所以js層定義的函數在oc端沒法執行。
其次,在webVIewDidFinishLoad階段建立JSContext並透出oc方法,因爲加載js階段在webVIewDidFinishLoad階段以前,所以一級頁面js沒法調用oc方法,可是二級頁面同理也是如此,可是因爲js代碼是在iOS的UI線程執行,所以爲了讓js能夠調用oc方法,能夠經過在js設置setTimeout來讓任務放到執行隊列的末端,先執行oc層的webVIewDidFinishLoad方法,待任務完成後再執行js中的異步代碼,經過這種方式能夠完成js調用oc方法;反過來,oc層調用js函數沒有任何問題,由於在webVIewDidFinishLoad階段js代碼已執行完畢(除了異步代碼)。
爲此,能夠經過實現一個簡易的框架來完成js層和oc層的交互,爲了更好的兼容性,只有在webVIewDidFinishLoad階段建立JSContext。而在js層則有兩種方式來監測並執行oc的方法:
1,在oc層的webVIewDidFinishLoad階段,暴露oc接口以後,經過JSContext或者UIWebView的stringByEvluateJavascriptString方法構建一個webViewDidFinishLoad
事件,js端進行偵聽並調用
2,簡單的經過setTimeout將js的執行順序排至隊列末端
經過上述方法,構建了一個簡單的JSCBridge,可是缺點也很明顯,對oc端接口暴露時機有硬性要求,而且js執行oc端的代碼始終是異步,有違咱們的初衷。android
在下一節中,介紹利用JSC高效通訊的另外一種hack方法,請期待!
web