when OC love JS

iOS7之后,蘋果推出了JavaScriptCore,是WebKit的一個重要組成部分,對JS進(jìn)行解析和提供運(yùn)行環(huán)境。

JSContext

JS上下文,提供運(yùn)行環(huán)境,同時也通過JSVirtualMachine管理著所有對象的生命周期,每個JSValue都和JSContext相關(guān)聯(lián)并且強(qiáng)引用context。

JSValue

JS和OC互換的橋梁,它提供了多種方法實(shí)現(xiàn)OC與JS之間的互換。

JSManagedValue

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

JSVirtualMachine

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

JSExport

一個協(xié)議,如果JS對象想直接調(diào)用OC對象里面的方法和屬性,那么這個OC對象只要實(shí)現(xiàn)這個JSExport協(xié)議就可以了。

OC中調(diào)用JS

從JS環(huán)境里取函數(shù)或者變量,就是將一段JS代碼通過JSContext轉(zhuǎn)化為OC執(zhí)行。

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

當(dāng)JS代碼多,直接食用字符串轉(zhuǎn)化可能會出現(xiàn)錯誤,此時可以讀取本地.html/.js文件來實(shí)現(xiàn)

#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中調(diào)用OC

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

一般寫在UIWebView的delegate中

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

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方法被調(diào)用:%@",str);
}

@end

js.html文件中的JS代碼

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

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

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

實(shí)際應(yīng)用中,可能會遇見瀏覽webView圖片的情況,我的GitHub有相關(guān)demo可參考瀏覽webView圖片demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容