OC与JS交互(一) ~~~~~ JavaScriptCore

前言

  前段时间作app活动,发现与JS交互方面有些混乱,为了梳理交互操做,又从新看了一遍原生与JS交互方面的东西javascript

  同时找到了下面参考文档中的深刻浅出和全面解析写的还不错,多余的就不赘述了,本身总结一下,不想看的看官门能够跳过【我的总结】这一部分html

我的总结

  • JavaScriptCore是iOS7.0+  macOS10.9+
  • JavaScriptCore中包括四个类:JSVirtualMachine、JSContext、JSManagedValue、JSValue
  • JSVirtualMachine实例表示JavaScript执行的独立环境,使用这个类有两个主要目的:支持并发JavaScript执行,以及管理在JavaScript和Objective-C或Swift之间桥接的对象的内存
  • JSVirtualMachine实例能够包括多个JSContext,JSContext之间能够传值(JSValue对象),JSVirtualMachine之间不能传值
  • 想要并发执行JavaScript,须要为每一个线程建立单独的JSVirtualMachine实例
  • 使用JSManagedValue来管理JavaScript值,并将被管理值的全部权交给JavaScriptCore virtual machine
  • JSManagedValue是对JSValue的包装,加入了“有条件保留”的行为,从而达到自动管理的目的
    • JavaScript值能够经过JavaScript对象访问(既不受JavaScript回收机制影响)
    • JsManegedValue能够经过OC或Swift进行访问
  • 能够理解为JSManagedValue是对JSValue的弱引用

 交互操做

  

  首先要声明获取JSContext对象,有下面三个方式,三选一java

  

- (void)webViewDidFinishLoad:(UIWebView *)webView
{

    // 1.这种方式须要传入一个JSVirtualMachine对象,若是传nil,会致使应用崩溃的。
    JSVirtualMachine *JSVM = [[JSVirtualMachine alloc] init];
    JSContext *JSCtx = [[JSContext alloc] initWithVirtualMachine:JSVM];

    // 2.这种方式,内部会自动建立一个JSVirtualMachine对象,能够经过        JSCtx.virtualMachine
    // 看其是否建立了一个JSVirtualMachine对象。
    JSContext *JSCtx = [[JSContext alloc] init];

    // 3. 经过webView的获取JSContext。
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

}

JSContext对象对应一个全局对象(global object)。例如web浏览器中的JSContext,起全局对象就是window对象。全局变量是全局对象的属性,能够经过JSValue对象或者context下标的方式来访问。ios

   因此感受用方法3,去获取而不是建立更好一些。web

 

  JavaScript 与 Objective-C 交互主要经过2种方式:浏览器

    block方式:使用block将响应方法暴露给JavaScript,从而完成Objective-C的一些操做markdown

    JSExport 协议:经过继承JSExport协议的方式来导出指定的方法和属性,能够将OC的中某个对象直接暴露给JS使用,并且在JS中使用就像调用JS的对象同样天然。并发

 

 block方式

    OC变量 <---> JS变量app

//执行的JS操做  为a赋值
JSValue *value = [context evaluateScript:@"var a = 1+2*3;"];

//获取a的值的三种方法
NSLog(@"a = %@", [context objectForKeyedSubscript:@"a"]);//经过context的实力方法objectForKeyedSubscript
NSLog(@"a = %@", [context.globalObject objectForKeyedSubscript:@"a"]);//经过context.globalObject的ObjectForKeyedSubscript实例方法
NSLog(@"a = %@", context[@"a"]); //经过下标

//输出结果Output: 
a = 7 
a = 7 
a = 7

 

  JS方法  --->  OC方法 async

     JS方法

//点击调用shareClick方法
<input type="button" value="分享" onclick="shareClick()" />

//shareClick方法
function shareClick() {
    //调用原生share方法

    //调用方法不带参数
    share();

    //调用方法带参数
    share('测试分享的标题','测试分享的内容','url=http://www.baidu.com');
}

 

    OC方法

- (void)addShareWithContext:(JSContext *)context
{
    context[@"share"] = ^() {
        //从JavaScript代码传过来的参数
        NSArray *args = [JSContext currentArguments];
        //参数的一些处理
        if (args.count < 3) {
            return ;
        }
        NSString *title = [args[0] toString];
        NSString *content = [args[1] toString];
        NSString *url = [args[2] toString];

// 在这里执行分享的操做
}; }

 

  OC方法  --->  JS方法

     JS代码

function shareResult(channel_id,share_channel,share_url) {
    //拼接数据
    var content = channel_id+","+share_channel+","+share_url;
                asyncAlert(content);
    //修改当前returnValue的值
    document.getElementById("returnValue").value = content;
}

 

    OC代码 

- (void)addShareWithContext:(JSContext *)context
{
    context[@"share"] = ^() {
        NSArray *args = [JSContext currentArguments];
        
        if (args.count < 3) {
            return ;
        }
        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];
    };
}

    

    这样的话也完成了JS的回调,既调用JS中的方法处理返回值

 

 JSExport 协议

简单的说就是写一个继承JSExport的协议,写一个类遵循该协议,将这个类做为一个变量交给JavaScriptCore。这样JavaScriptCore会自动认定继承JSExport这个类的协议为要导入到JavaScript的方法和属性列表,从而进行导入。

代码1显示了协议中的变量,方法和类方法。代码2显示了对应的JavaScript中调用的方法

    代码1

@protocol MyPointExports <JSExport>
//对于每一个导出的Objective-C属性,JavaScriptCore都会在原型上建立JavaScript访问器属性。
@property double x;
@property double y;
//对于每一个导出的实例方法,JavaScriptCore都会建立一个相应的JavaScript函数做为原型对象的属性。
- (NSString *)description;
- (instancetype)initWithX:(double)x y:(double)y;
//对于每一个导出的类方法,JavaScriptCore在构造函数对象上建立一个JavaScript函数
+ (MyPoint *)makePointWithX:(double)x y:(double)y;
@end

    代码2

// Objective-C properties become fields.
point.x;
point.x = 10;
// Objective-C instance methods become functions.
point.description();
// Objective-C initializers can be called with constructor syntax.
var p = MyPoint(1, 2);
// Objective-C class methods become functions on the constructor object.
var q = MyPoint.makePointWithXY(0, 0);

    其中MyPoint为遵循MyPointExports的类

    

    你可能注意到了,后两个方法参数写法上彷佛有些变化。    

    如上面英文提到的,初始化方法能够用构造函数方法调用

    而多参数问题,导入到JavaScript时由这么一个规则

    • 全部冒号都从方法选择器中删除
    • 任何后面冒号的小写字母都变成大写    

    例如:OC中为  doFoo:withBar:

       JavaScript中为  doFooWithBar 

 

    很差记? 苹果又贴心的给了一个宏来指定导出到Javascript的方法名 

@protocol MyClassJavaScriptMethods <JSExport>
JSExportAs(doFoo,
- (void)doFoo:(id)foo withBar:(id)bar
);
@end

 

    到此,用法方面就结束了

  

后记

其实若是你看了下面的参考文档,你就会以为他们确实写的很全面,我再写这一篇的时候也这么以为,一样也犹豫过,在很显然不如别人写的好的状况下要不要在写下去。

后来想清了一个问题,虽然说不要重复造轮子,可是不会造轮子的话,永远只能当组装师搬运工,学会造轮子以后才能成为创造者。一千个读者就有一千个哈姆雷特,或许你的切入点、视角更好呢,就算不是完美,至少你走过了这一步,也印象更深入了些。虽然不完美,但仍是写完了,但愿可以对你有所帮助。

PS:若是你看了官方文档,会发现,大多数信息源仍是官方文档~~~

参考文档

  JavaScriptCore官方文档

  深刻浅出 JavaScriptCore

  JavaScriptCore全面解析 (上篇)

  JavaScriptCore全面解析 (下篇)

  iOS UIWebView与JavaScript交互之JavaScriptCore

  iOS下JS与OC互相调用(四)--JavaScriptCore

相关文章
相关标签/搜索