OC 与 JS 的交互

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011374318/article/details/79418575

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 type JavaScript type
nil undefined
NSNull null
NSString string
NSNumber number, boolean
NSDictionary Object object
NSArray Array object
NSDate Date object
NSBlock Function object
id Wrapper object
Class Constructor 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 代码间的交互,感兴趣的可以下载工程,自己尝试一下。

猜你喜欢

转载自blog.csdn.net/u011374318/article/details/79418575