OC與JS交互(一) ~~~~~ JavaScriptCore

前言

  前段時間作app活動,發現與JS交互方面有些混亂,爲了梳理交互操做,又從新看了一遍原生與JS交互方面的東西javascript

  同時找到了下面參考文檔中的深刻淺出和全面解析寫的還不錯,多餘的就不贅述了,本身總結一下,不想看的看官門能夠跳過【我的總結】這一部分html

我的總結

  • JavaScriptCore是iOS7.0+  macOS10.9+
  • JavaScriptCore中包括四個類:JSVirtualMachine、JSContext、JSManagedValue、JSValue
  • JSVirtualMachine實例表示JavaScript執行的獨立環境,使用這個類有兩個主要目的:支持併發JavaScript執行,以及管理在JavaScript和Objective-C或Swift之間橋接的對象的內存
  • JSVirtualMachine實例能夠包括多個JSContext,JSContext之間能夠傳值(JSValue對象),JSVirtualMachine之間不能傳值
  • 想要併發執行JavaScript,須要爲每一個線程建立單獨的JSVirtualMachine實例
  • 使用JSManagedValue來管理JavaScript值,並將被管理值的全部權交給JavaScriptCore virtual machine
  • JSManagedValue是對JSValue的包裝,加入了「有條件保留」的行爲,從而達到自動管理的目的
    • JavaScript值能夠經過JavaScript對象訪問(既不受JavaScript回收機制影響)
    • JsManegedValue能夠經過OC或Swift進行訪問
  • 能夠理解爲JSManagedValue是對JSValue的弱引用

 交互操做

  

  首先要聲明獲取JSContext對象,有下面三個方式,三選一java

  

- (void)webViewDidFinishLoad:(UIWebView *)webView
{

    // 1.這種方式須要傳入一個JSVirtualMachine對象,若是傳nil,會致使應用崩潰的。
    JSVirtualMachine *JSVM = [[JSVirtualMachine alloc] init];
    JSContext *JSCtx = [[JSContext alloc] initWithVirtualMachine:JSVM];

    // 2.這種方式,內部會自動建立一個JSVirtualMachine對象,能夠經過        JSCtx.virtualMachine
    // 看其是否建立了一個JSVirtualMachine對象。
    JSContext *JSCtx = [[JSContext alloc] init];

    // 3. 經過webView的獲取JSContext。
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

}

JSContext對象對應一個全局對象(global object)。例如web瀏覽器中的JSContext,起全局對象就是window對象。全局變量是全局對象的屬性,能夠經過JSValue對象或者context下標的方式來訪問。ios

   因此感受用方法3,去獲取而不是建立更好一些。web

 

  JavaScript 與 Objective-C 交互主要經過2種方式:瀏覽器

    block方式:使用block將響應方法暴露給JavaScript,從而完成Objective-C的一些操做markdown

    JSExport 協議:經過繼承JSExport協議的方式來導出指定的方法和屬性,能夠將OC的中某個對象直接暴露給JS使用,並且在JS中使用就像調用JS的對象同樣天然。併發

 

 block方式

    OC變量 <---> JS變量app

//執行的JS操做  爲a賦值
JSValue *value = [context evaluateScript:@"var a = 1+2*3;"];

//獲取a的值的三種方法
NSLog(@"a = %@", [context objectForKeyedSubscript:@"a"]);//經過context的實力方法objectForKeyedSubscript
NSLog(@"a = %@", [context.globalObject objectForKeyedSubscript:@"a"]);//經過context.globalObject的ObjectForKeyedSubscript實例方法
NSLog(@"a = %@", context[@"a"]); //經過下標

//輸出結果Output: 
a = 7 
a = 7 
a = 7

 

  JS方法  --->  OC方法 async

     JS方法

//點擊調用shareClick方法
<input type="button" value="分享" onclick="shareClick()" />

//shareClick方法
function shareClick() {
    //調用原生share方法

    //調用方法不帶參數
    share();

    //調用方法帶參數
    share('測試分享的標題','測試分享的內容','url=http://www.baidu.com');
}

 

    OC方法

- (void)addShareWithContext:(JSContext *)context
{
    context[@"share"] = ^() {
        //從JavaScript代碼傳過來的參數
        NSArray *args = [JSContext currentArguments];
        //參數的一些處理
        if (args.count < 3) {
            return ;
        }
        NSString *title = [args[0] toString];
        NSString *content = [args[1] toString];
        NSString *url = [args[2] toString];

// 在這裏執行分享的操做
}; }

 

  OC方法  --->  JS方法

     JS代碼

function shareResult(channel_id,share_channel,share_url) {
    //拼接數據
    var content = channel_id+","+share_channel+","+share_url;
                asyncAlert(content);
    //修改當前returnValue的值
    document.getElementById("returnValue").value = content;
}

 

    OC代碼 

- (void)addShareWithContext:(JSContext *)context
{
    context[@"share"] = ^() {
        NSArray *args = [JSContext currentArguments];
        
        if (args.count < 3) {
            return ;
        }
        NSString *title = [args[0] toString];
        NSString *content = [args[1] toString];
        NSString *url = [args[2] toString];
        // 在這裏執行分享的操做
        
        // 將分享結果返回給js
        NSString *jsStr = [NSString stringWithFormat:@"shareResult('%@','%@','%@')",title,content,url];
        [[JSContext currentContext] evaluateScript:jsStr];
    };
}

    

    這樣的話也完成了JS的回調,既調用JS中的方法處理返回值

 

 JSExport 協議

簡單的說就是寫一個繼承JSExport的協議,寫一個類遵循該協議,將這個類做爲一個變量交給JavaScriptCore。這樣JavaScriptCore會自動認定繼承JSExport這個類的協議爲要導入到JavaScript的方法和屬性列表,從而進行導入。

代碼1顯示了協議中的變量,方法和類方法。代碼2顯示了對應的JavaScript中調用的方法

    代碼1

@protocol MyPointExports <JSExport>
//對於每一個導出的Objective-C屬性,JavaScriptCore都會在原型上建立JavaScript訪問器屬性。
@property double x;
@property double y;
//對於每一個導出的實例方法,JavaScriptCore都會建立一個相應的JavaScript函數做爲原型對象的屬性。
- (NSString *)description;
- (instancetype)initWithX:(double)x y:(double)y;
//對於每一個導出的類方法,JavaScriptCore在構造函數對象上建立一個JavaScript函數
+ (MyPoint *)makePointWithX:(double)x y:(double)y;
@end

    代碼2

// Objective-C properties become fields.
point.x;
point.x = 10;
// Objective-C instance methods become functions.
point.description();
// Objective-C initializers can be called with constructor syntax.
var p = MyPoint(1, 2);
// Objective-C class methods become functions on the constructor object.
var q = MyPoint.makePointWithXY(0, 0);

    其中MyPoint爲遵循MyPointExports的類

    

    你可能注意到了,後兩個方法參數寫法上彷佛有些變化。    

    如上面英文提到的,初始化方法能夠用構造函數方法調用

    而多參數問題,導入到JavaScript時由這麼一個規則

    • 全部冒號都從方法選擇器中刪除
    • 任何後面冒號的小寫字母都變成大寫    

    例如:OC中爲  doFoo:withBar:

       JavaScript中爲  doFooWithBar 

 

    很差記? 蘋果又貼心的給了一個宏來指定導出到Javascript的方法名 

@protocol MyClassJavaScriptMethods <JSExport>
JSExportAs(doFoo,
- (void)doFoo:(id)foo withBar:(id)bar
);
@end

 

    到此,用法方面就結束了

  

後記

其實若是你看了下面的參考文檔,你就會以爲他們確實寫的很全面,我再寫這一篇的時候也這麼以爲,一樣也猶豫過,在很顯然不如別人寫的好的狀況下要不要在寫下去。

後來想清了一個問題,雖然說不要重複造輪子,可是不會造輪子的話,永遠只能當組裝師搬運工,學會造輪子以後才能成爲創造者。一千個讀者就有一千個哈姆雷特,或許你的切入點、視角更好呢,就算不是完美,至少你走過了這一步,也印象更深入了些。雖然不完美,但仍是寫完了,但願可以對你有所幫助。

PS:若是你看了官方文檔,會發現,大多數信息源仍是官方文檔~~~

參考文檔

  JavaScriptCore官方文檔

  深刻淺出 JavaScriptCore

  JavaScriptCore全面解析 (上篇)

  JavaScriptCore全面解析 (下篇)

  iOS UIWebView與JavaScript交互之JavaScriptCore

  iOS下JS與OC互相調用(四)--JavaScriptCore

相關文章
相關標籤/搜索