zoukankan      html  css  js  c++  java
  • iOS下JS与OC互相调用(四)--JavaScriptCore

    1、简要介绍JavaScriptCore

    JavaScriptCore是一个iOS 7 新添加的框架,使用前需要先导入JavaScriptCore.framework
    然后我们在JavaScriptCore.h中可以看到,该框架主要的类就只有五个:

    1.1 JSVirtualMachine 
    JSVirtualMachine看名字直译是JS 虚拟机,也就是说JavaScript是在一个虚拟的环境中执行,而JSVirtualMachine为其执行提供底层资源。

    1.2 JSContext
    JSContext是为JavaScript的执行提供运行环境,所有的JavaScript的执行都必须在JSContext环境中。JSContext也管理JSVirtualMachine中对象的生命周期。每一个JSValue对象都要强引用关联一个JSContext。当与某JSContext对象关联的所有JSValue释放后,JSContext也会被释放。
    通常情况下我们一般都这样创建JSContext:
    //  通过webView的获取JSContext。
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    1.3 JSValue
    JSValue都是通过JSContext返回或者创建的,并没有构造方法。JSValue包含了每一个JavaScript类型的值,通过JSValue可以将Objective-C中的类型转换为JavaScript中的类型,也可以将JavaScript中的类型转换为Objective-C中的类型。
    OC、JSValue、JavaScript的类型对应关系表:
     
    1.4 JSManagedValue
    JSManagedValue主要用途是解决JSValue对象在Objective-C 堆上的安全引用问题。把JSValue 保存进Objective-C 堆对象中是不正确的,这很容易引发循环引用,而导致JSContext不能释放。 这个类主要是将JSValue对象转换为JSManagedValue的API,而且也不常用,就不做具体介绍了。
    1.5 JSExport
    JSExport是一个协议类,但是该协议并没有任何属性和方法。 怎么使用呢? 我们可以自定义一个协议类,继承自JSExport。无论我们在JSExport里声明的属性,实例方法还是类方法,继承的协议都会自动的提供给任何 JavaScript 代码。 So,我们只需要在自定义的协议类中,添加上属性和方法就可以了。

    JS调用OC分两种情况

    一,js里面直接调用方法(Block方式)

    二,js里面通过对象调用方法(JSExport协议方式)

    先说第一种吧:

    创建UIWebView:

    self.webView = [[UIWebView alloc] initWithFrame:self.view.frame];
        self.webView.delegate = self;
        NSURL *htmlURL = [[NSBundle mainBundle] URLForResource:@"index.html" withExtension:nil];
    //    NSURL *htmlURL = [NSURL URLWithString:@"http://www.baidu.com"];
        NSURLRequest *request = [NSURLRequest requestWithURL:htmlURL];
    
        // 如果不想要webView 的回弹效果
        self.webView.scrollView.bounces = NO;
        // UIWebView 滚动的比较慢,这里设置为正常速度
        self.webView.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;
        [self.webView loadRequest:request];
        [self.view addSubview:self.webView];

    HTML的内容也大致一样,不过JS的调用有些区别,更简单了。

    function shareClick() {
        share('测试分享的标题','测试分享的内容','url=http://www.baidu.com');
    }
    
    function shareResult(channel_id,share_channel,share_url) {
        var content = channel_id+","+share_channel+","+share_url;
        asyncAlert(content);
        document.getElementById("returnValue").value = content;
    }
    
    function locationClick() {
        getLocation();
    }
    
    function setLocation(location) {
        asyncAlert(location);
        document.getElementById("returnValue").value = location;
    }
    添加JS要调用的原生OC方法
    #pragma mark - UIWebViewDelegate
    - (void)webViewDidFinishLoad:(UIWebView *)webView
    {
        NSLog(@"webViewDidFinishLoad");
    
        [self addCustomActions];
    }

    将所有要添加的功能方法,集中到一个方法addCustomActions中,便于维护

    #pragma mark - private method
    - (void)addCustomActions
    {
        JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    
        [self addScanWithContext:context];
    
        [self addLocationWithContext:context];
    
        [self addSetBGColorWithContext:context];
    
        [self addShareWithContext:context];
    
        [self addPayActionWithContext:context];
    
        [self addShakeActionWithContext:context];
    
        [self addGoBackWithContext:context];
    }

    然后每一个小功能独立开来,这样修改和解决Bug的时候能够快速定位到某个功能

    - (void)addShareWithContext:(JSContext *)context
    {
        __weak typeof(self) weakSelf = self;
        context[@"share"] = ^() {
            NSArray *args = [JSContext currentArguments];
    
            if (args.count < 3) {
                return ;
            }
    //        这个地方取到的值是jsvalue,需要转化一下
            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];
        };
    }
    注意: 1.JS要调用的原生OC方法,可以在viewDidLoad webView被创建后就添加好,但最好是在网址加载成功后再添加,以避免无法预料的乱入Bug。
    2.block 中的执行环境是在子线程中。奇怪的是竟然可以更新部分UI,例如给view设置背景色,调用webView执行js等,但是弹出原生alertView就会在控制台报子线程操作UI的错误信息。
    3.避免循环引用,因为block 会持有外部变量,而JSContext也会强引用它所有的变量,因此在block中调用self时,要用__weak 转一下。而且在block内不要使用外部的context 以及JSValue,都会导致循环引用。如果要使用context 可以使用[JSContext currentContext]。当然我们可以将JSContext 和JSValue当做block的参数传进去,这样就可以使用啦。
     
    OC调用JS方法:

    OC调用JS方法就有多种方式了。首先介绍使用JavaScriptCore框架的方式。
    方式1 
    使用JSContext的方法-evaluateScript,可以实现OC调用JS方法。
    下面是一个调用JS中payResult方法的示例代码:

    NSString *jsStr = [NSString stringWithFormat:@"payResult('%@')",@"支付成功"];
    [[JSContext currentContext] evaluateScript:jsStr];

    方式2 
    使用JSValue的方法-callWithArguments,也可以实现OC调用JS方法。
    下面这个示例代码依然是调用JS中的payResult:

    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    
    [context[@"payResult"] callWithArguments:@[@"支付弹窗"]];

    当然,如果是在执行原生OC方法之后,想要在OC执行完操作后,将结果回调给JS时,可以这样写:

    - (void)addPayActionWithContext:(JSContext *)context
    {
        context[@"payAction"] = ^() {
            NSArray *args = [JSContext currentArguments];
            
            if (args.count < 4) {
                return ;
            }
            
            NSString *orderNo = [args[0] toString];
            NSString *channel = [args[1] toString];
            long long amount = [[args[2] toNumber] longLongValue];
            NSString *subject = [args[3] toString];
            
            // 支付操作
            NSLog(@"orderNo:%@---channel:%@---amount:%lld---subject:%@",orderNo,channel,amount,subject);
            
            // 将支付结果返回给js
    //        NSString *jsStr = [NSString stringWithFormat:@"payResult('%@')",@"支付成功"];
    //        [[JSContext currentContext] evaluateScript:jsStr];
            //自动回调
            [[JSContext currentContext][@"payResult"] callWithArguments:@[@"支付成功"]];
        };
    }
    方式3      利用UIWebView的API:
    NSString *jsStr = [NSString stringWithFormat:@"payResult('%@')",@"支付成功"];
    [self.webView stringByEvaluatingJavaScriptFromString:jsStr];

    第一种方式到此介绍完毕.

    下面介绍第二种<JSExport>协议的方式:

    首先我们新建一个类,JSNativeMethod:

    .h 声明

    #import <Foundation/Foundation.h>
    #import <JavaScriptCore/JavaScriptCore.h>
    
    
    //首先创建一个实现了JSExport协议的协议
    @protocol TestJSObjectProtocol <JSExport>
    
    //此处我们测试几种参数的情况
    - (NSString *)imgCallBack:(NSString *)url;
    
    // 通过JSON传过来
    - (void)callWithDict:(NSDictionary *)params;
    
    @end
    
    @interface JSNativeMethod : NSObject<TestJSObjectProtocol>
    
    @end

    .m实现

    @implementation JSNativeMethod
    
    - (NSString *)imgCallBack:(NSString *)url {
      NSLog(@"touch image %@",url);
      return @"iOS To H5";
    }
    - (void)callWithDict:(NSDictionary *)params {
      
      NSLog(@"%@",params);
      JSValue *jsFunc = self.jsContext[@"uploadimage"];
      [jsFunc callWithArguments:@[@{@"image":@"image upload success"}]];
    }
    @end

    然后在WebView代理方法里:

    #pragma mark - UIWebViewDelegate
    - (void)webViewDidFinishLoad:(UIWebView *)webView {
      // 获取当前JS运行环境
      self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
      
      /  JSNativeMethod *call = [[JSNativeMethod alloc] init];
      //将JSNativeMethod封装到JavaScript函数Native()中
      self.jsContext[@"Native"] = call;
    //JSContext 还有另外一个有用的招数:通过设置上下文的 exceptionHandler 属性,你可以观察和记录语法,类型以及运行时错误。 exceptionHandler 是一个接收一个 JSContext 引用和异常本身的回调处理
      self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
        context.exception = exceptionValue;
        NSLog(@"异常信息:%@", exceptionValue);
      };
    }

    利用这种方式:需要js那边同样需要Native:

    //app端callback callWithDict()
    function callWithDict(a) {
    log(JSON.stringify(a));
    window.Native.callWithDict(a);
    }

    OK,基本结束,这种方式demo还没整理好,只是在我之前项目做了部分修改,因涉及源码,demo暂时不上,以后补上.

     第一种方式:demo地址(Block形式):https://github.com/domanc/JS_OC_JavaScriptCore.git

     第二种方式: 待更新!

  • 相关阅读:
    ActiveSync合作关系对话框的配置
    WINCE对象存储区(object store)
    Wince 隐藏TASKBAR的方法
    Wince输入法换肤换语言机制
    poj 3080 Blue Jeans 解题报告
    codeforces A. Vasily the Bear and Triangle 解题报告
    hdu 1050 Moving Tables 解题报告
    hdu 1113 Word Amalgamation 解题报告
    codeforces A. IQ Test 解题报告
    poj 1007 DNA Sorting 解题报告
  • 原文地址:https://www.cnblogs.com/dianming/p/7160534.html
Copyright © 2011-2022 走看看