when OC love JS

iOS7之后,蘋果推出了JavaScriptCore,是WebKit的一個重要組成部分,對JS進行解析和提供運行環境。

JSContext

JS上下文,提供運行環境,同時也通過JSVirtualMachine管理著所有對象的生命周期,每個JSValue都和JSContext相關聯并且強引用context。

JSValue

JS和OC互換的橋梁,它提供了多種方法實現OC與JS之間的互換。

JSManagedValue

JS和OC對象的內存管理輔助對象。由于JS內存管理是垃圾回收,并且JS中的對象都是強引用,而OC是引用計數。如果雙方相互引用,勢必會造成循環引用,而導致內存泄露。我們可以用JSManagedValue保存JSValue來避免。

JSVirtualMachine

JS運行的虛擬機,有獨立的堆空間和垃圾回收機制。- (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine;,如果是使用- (id)init;進行初始化,那么在其內部會自動創建一個新的JSVirtualMachine對象然后調用前邊的初始化方法。

JSExport

一個協議,如果JS對象想直接調用OC對象里面的方法和屬性,那么這個OC對象只要實現這個JSExport協議就可以了。

OC中調用JS

從JS環境里取函數或者變量,就是將一段JS代碼通過JSContext轉化為OC執行。

#pragma mark - 加載一段JS代碼
-(void)OCCallJS
{
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    NSString *jsString = [NSString stringWithFormat:@"alert('%@')",@"這是OC調用JS"];
    [context evaluateScript:jsString];
}

當JS代碼多,直接食用字符串轉化可能會出現錯誤,此時可以讀取本地.html/.js文件來實現

#pragma mark - 加載本地.js文件
-(void)loadScriptWithContext:(JSContext *)context AndFileName:(NSString *)fileName
{
    NSString *filePath = [NSString stringWithFormat:@"%@/JS/%@",[[NSBundle mainBundle] resourcePath],fileName];
    NSString *script = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
    [context evaluateScript:script];
}
//加載.html文件
    NSURL *htmlURL = [[NSBundle mainBundle]URLForResource:@"js.html" withExtension:nil];
    NSURLRequest *request = [NSURLRequest requestWithURL:htmlURL];
    [self.webView loadRequest:request];

JS中調用OC

方法一:將block注入JSContext上下文,在JS執行的時候調用OC原生方法
方法二:將OC對象注入JSContext上下文,在JS執行的時候調用OC原生方法

一般寫在UIWebView的delegate中

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    //JS調OC方法一
    //首先創建JSContext 對象(此處通過當前webView的鍵獲取到jscontext)
    JSContext *context1 = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    context1[@"JSCallOCWithBlock"] = ^(NSString *str){
        NSLog(@"OC的Block被調用:%@",str);
    };
    
    //JS調OC方法二
    JSContext *context2 = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    GemJSObject *jsObject = [[GemJSObject alloc]init]; //創造一個對象
    jsObject.x = 10;
    context2[@"JSCallOCWithObject"] = jsObject; //將對象注入JS運行環境
}

OC對象中的代碼

#pragma mark - GemJSObject.h
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>

@protocol GemJSObjectExport <JSExport>
//定義要暴露給JS的屬性和方法,JS就可以對其操作了
@property int x;

-(void)nslog:(NSString *)str;

@end

@interface GemJSObject : NSObject<GemJSObjectExport>

-(void)nslog:(NSString *)str;

@end
#pragma mark - GemJSObject.m
#import "GemJSObject.h"

@implementation GemJSObject

@synthesize x;

-(void)nslog:(NSString *)str
{
    NSLog(@"%d",self.x * self.x);
    NSLog(@"OC的nslog方法被調用:%@",str);
}

@end

js.html文件中的JS代碼

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
            <title>test javascript</title>
            </head>
    <body>
        <div>
            <!--
             調用方法(并傳入參數,若block中無參,則此參數無用)
             -->
            <button onclick="JSCallOCWithBlock('JS調用OC方法,并傳參(通過block)');">JS_OC_Block</button>
            <br>
            <!--
             調用對象的nslog方法(并傳入參數),對象需要傳入自定義的NSObject對象(遵循JSExport協議)
             -->
            <button onclick="JSCallOCWithObject.nslog('JS調用OC方法,并傳參(通過GemJSObject對象)');">JS_OC_Object</button>
        </div>
    </body>
</html>
方法三:攔截非正常的URL
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    //JS調OC方法三:攔截不正常URL
    if ([request.URL.absoluteString isEqualToString:@"非正常的URL頭"]) {
        //調用OC的方法
        [self handleCustomAction:request.URL];
        return NO;
    }
    return YES;
}

-(void)handleCustomAction:(NSURL *)url
{
    if ([url.host isEqualToString:@"getLocation"]) {
        [self getLocation];
    }
}

-(void)getLocation
{
    //獲取位置信息,將結果返回給js
    NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')",@"北京市朝陽區XXX號"];
    [self.webView stringByEvaluatingJavaScriptFromString:jsStr];
    
    /**
     補充:在JS代碼中調OC,也需要傳參數到OC中,就像一個get請求一樣,把參數放在后面。
     function shareClick() {
     loadURL("haleyAction://shareClick?title=測試分享的標題&content=測試分享的內容&url=http://www.baidu.com");
     }
     */
}

實際應用中,可能會遇見瀏覽webView圖片的情況,我的GitHub有相關demo可參考瀏覽webView圖片demo

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 本文由我們團隊的 糾結倫 童鞋撰寫。 寫在前面 本篇文章是對我一次組內分享的整理,大部分圖片都是直接從keynot...
    知識小集閱讀 15,281評論 11 172
  • 寫在前面 本篇文章是對我一次組內分享的整理,大部分圖片都是直接從keynote上截圖下來的,本來有很多炫酷動效的,...
    等開會閱讀 14,502評論 6 69
  • 注:本文copy自http://www.lxweimin.com/p/ac534f508fb0,純屬當筆記使用。 概...
    BookKeeping閱讀 742評論 1 3
  • 隨著H5技術的興起,在iOS開發過程中,難免會遇到原生應用需要和H5頁面交互的問題。其中會涉及方法調用及參數傳值等...
    Chris_js閱讀 3,109評論 1 8
  • 聽了易仁永澄老師的分享會《成為高效能人士,你只差這一個習慣》,最直接的感受就是我應該知道自己知道自己在吃飯,但其實...
    大江中的小魚兒閱讀 512評論 0 0