OC 与 JS 的交互

JavaScriptCore

从 iOS 7 开始,苹果引入了 JavaScriptCore 框架,用于原生代码同 JavaScript 脚本之间的数据交互,方便了原生和网页功能的混合开发。

要实现 JavaScript 中的方法与原生方法之间的相互调用,关键在于 JSContext 类以及 JSExport 协议。

JSContext

JSContext 是 JavaScript 的执行环境,所有的 JavaScript 函数执行都发生在该上下文中,所有的 JavaScript 值都与之相关联。

该类的关键是下面四个类方法,这几个类方法的调用都应该是由 JavaScript 的函数调用引起的。

  • + (JSContext *)currentContext; 获取当前执行上下文,如果没有,返回 nil 。
  • + (JSValue *)currentCallee; 获取当前执行的 JavaScript 函数。
  • + (JSValue *)currentThis; 获取当前执行方法的 this
  • + (NSArray *)currentArguments; 获取传递给当前回调方法的参数

JSValue

JSValue 描述的是 JavaScript 中的值,这个值始终关联着一个 JSContext 对象,或者说这个值总是来源于某一个上下文。
OC 方法与 JavaScript 函数间的交互涉及到变量类型的转换,JSValue 便是用来处理这种转换的,下表是它们变量间的对应关系:

Objective-C typeJavaScript type
nilundefined
NSNullnull
NSStringstring
NSNumbernumber, boolean
NSDictionaryObject object
NSArrayArray object
NSDateDate object
NSBlockFunction object
idWrapper object
ClassConstructor object

在该类中除了类型转换的方法外,还提供了三个方法用于实现 JavaScript 函数的调用。

  • - (JSValue *)callWithArguments:(NSArray *)arguments;

    将 JSValue 本身当作一个 JavaScript 函数,并执行该 JavaScript 函数。

  • - (JSValue *)constructWithArguments:(NSArray *)arguments;

    调用 JavaScript 中的构造器方法,创建一个新的变量并返回。

  • - (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments;

    访问该变量的名为 method 的成员变量,并且将该成员变量当作一个函数进行调用,且返回该函数的调用结果。

JSExport

当获取了 JSContext 对象后,可以直接绑定变量或 block 代码块到该上下文中,这样,在 JavaScript 中就可以使用这些绑定的变量或者调用这些代码块。但是,这样做并不符合面向对象的思想,并且代码也不好维护。所以,该框架给出了 JSExport 协议,这个协议并没有什么需要实现的方法,而是我们自定义一个继承该协议的子协议。

在自定义的子协议中,声明需要的属性和方法。而后,创建一个遵循该子协议的类,那么将该自定义类的实例绑定到 JSContext 上下文中时,框架便会将子协议中的变量和方法导入到 JavaScript 中,但是该类中其他不在子协议中的属性和方法并不能被 JavaScript 访问。

例程

自定义一个 CustomJSExport 协议,和一个遵循该协议的 CustomSave 类,如下:

@protocol CustomJSExport <JSExport>

@property (strong, nonatomic) NSString *name;

- (void)saveValue;

@end

@interface CustomSave : NSObject  <CustomJSExport>

@property (strong, nonatomic) NSString *name;

@end

实现协议中的方法如下:

- (void)saveValue {

    NSLog(@"currentArguments %@",[JSContext currentArguments]);

    JSValue *currentThis = [JSContext currentThis];
    NSLog(@"currentThis %@",currentThis);

    JSValue *currentCallee = [JSContext currentCallee];
    NSLog(@"currentCallee %@",currentCallee);

    [[JSContext currentContext][@"clear"] callWithArguments:nil];
}

创建一个包含 UIWebView 的控制器,并实现 UIWebViewDelegate 中的如下方法:

#pragma mark - UIWebViewDelegate
- (void)webViewDidFinishLoad:(UIWebView *)webView {

     //这个属性路径:UIWebBrowserView => WebView => WebFrame => JSContext
    JSContext *context = [webView valueForKeyPath: @"documentView.webView.mainFrame.javaScriptContext"];    

    context[@"saveValue"] = ^(){

        NSLog(@"currentArguments %@",[JSContext currentArguments]);

        JSContext *currentContext = [JSContext currentContext];

       [currentContext[@"show"] callWithArguments:nil];

    };


    CustomSave *save = [[CustomSave alloc]init];
    save.name = @"Martin";

    context[@"CustomSave"] = save;

    context[@"age"] = @"20";

}

启动应用时,让 UIWebView 加载下面的 HTML 文件:

<!DOCTYPE html>
<html>

    <head>

        <meta charset="UTF-8">

    </head>

    <body>

        <a>this is a test page.</a>

        <br>

        <input type="text" size="30" id="content">

        <input type="submit" value="show" onclick="saveValue('test1','test2')">

        <input type="submit" value="clear" onclick="CustomSave.saveValue('hello','world')">        

    </body>

    <script type='text/javascript'>

        function show() {
            var content = "His name is " + CustomSave.name + " and age is " + this.age;            
            document.getElementById('content').value = content;            
        }

        function clear() {            
            document.getElementById('content').value = '';            
        }
    </script>

</html>

在上面的 HTML 文件中的 CustomSave 变量就是在 webViewDidFinishLoad: 方法中创建的 CustomSave 类实例。并且,对于 JavaScript 中调用 OC 中的方法,其参数并没有严格的限制。

这是一个简单的例子,用于说明原生代码同 JavaScript 代码间的交互,感兴趣的可以下载工程,自己尝试一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值