美文网首页
JavaScriptCore的基本用法(二)

JavaScriptCore的基本用法(二)

作者: 七里小晴天 | 来源:发表于2016-06-14 15:28 被阅读279次

    代理设置(JS调用OC的第二种方法)

    h文件

    //首先写一个协议  遵守JSExport协议
    @protocol JSExportTest <JSExport>
    //宏转换下,将JS函数名称指定为Add;
    JSExportAs(add, - (NSInteger)add:(NSInteger)a b:(NSInteger)b);
    @property (nonatomic, assign) NSInteger sum;
    
    @end
    
    
    //建一个对象实现这个协议
    @interface JSTest : NSObject<JSExportTest>
    
    @end
    

    m文件

    @implementation JSTest
    @synthesize sum = _sum;
    //实现协议方法
    - (NSInteger)add:(NSInteger)a b:(NSInteger)b{
        return a + b;
    }
    
    
    -(void)setSum:(NSInteger)sum{
        NSLog(@"%ld",(long)sum);
        _sum = sum;
    }
    
    @end
    

    在viewcontroller里面

        JSContext *context = [[JSContext alloc] init];
        //设置异常处理
        self.context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
            [JSContext currentContext].exception = exception;
            NSLog(@"exception:%@",exception);
        };
        //将obj添加到context中
        scontext[@"obj"] = [][JSTest alloc]init];
        //JS里面调用obj方法,并将结果赋值给obj的sum属性
        [context evaluateScript:@"obj.sum = obj.add(2,3)"];
        
    

    在JS中进行调用这个对象的方法,并将结果赋值sum。唯一要注意的是OC的函数命名和JS函数命名规则问题。协议中定义的是add: b:,但是JS里面方法名字是add(a,b)。可以通过JSExportAs这个宏转换成JS的函数名字。

    异常处理

    Objective-C的异常会在运行时被Xcode捕获,而在JSContext中执行的JavaScript如果出现异常,只会被JSContext捕获并存储在exception属性上,而不会向外抛出。时时刻刻检查JSContext对象的exception是否不为nil显然是不合适,更合理的方式是给JSContext对象设置exceptionHandler,它接受的是^(JSContext *context, JSValue *exceptionValue)形式的Block。其默认值就是将传入的exceptionValue赋给传入的context的exception属性:

    JSContext *context = [[JSContext alloc] init];
    context.exceptionHandler = ^(JSContext *con, JSValue *exception) {
        NSLog(@"%@", exception);
        con.exception = exception;
    };
     
    [context evaluateScript:@"fengzhen = 66"];
     
    //输出:
    //  ReferenceError: Can't find variable: fengzhen
    

    无论是把Block传给JSContext对象让其变成JavaScript方法,还是把它赋给exceptionHandler属性,在Block内都不要直接使用其外部定义的JSContext对象或者JSValue,应该将其当做参数传入到Block中,或者通过JSContext的类方法+ (JSContext *)currentContext;来获得。否则会造成循环引用使得内存无法被正确释放。

    内存管理

    OC使用的是ARC,JS使用的是垃圾回收机制,js的引用全都是强引用,垃圾回收机制会帮他们打破这种强引用,所以JS不存在循环引用的问题。一般情况下,OC和JS对象之间内存管理都无需我们去关心。不过还是有几个注意点需要我们去留意下。

    1、不要在block里面直接使用context,或者使用外部的JSValue对象。
      JSContext *context = [[JSContext alloc] init];
        //设置异常处理
        self.context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
        //直接这么使用是错误的
        //context.exception = exception;
            [JSContext currentContext].exception = exception;
            NSLog(@"exception:%@",exception);
        };
    
    2.OC对象不要用属性直接保存JSValue对象,因为这样太容易造成循环引用。

    下面的例子:

    #import <Foundation/Foundation.h>
    #import <JavaScriptCore/JavaScriptCore.h>
    //首先写一个协议  遵守JSExport协议
    @protocol JSExportTest <JSExport>
    //宏转换下,将JS函数名称指定为Add;
    JSExportAs(add, - (NSInteger)add:(NSInteger)a b:(NSInteger)b);
    @property (nonatomic, strong) JSValue *value;
    
    @end
    
    
    //建一个对象实现这个协议
    @interface JSTest : NSObject<JSExportTest>
    
    @end
    
    #import "JSTest.h"
    
    @implementation JSTest
    @synthesize value = _value;
    //实现协议方法
    
    -(void)setValue:(JSValue *)value{
        _value = value;
    }
    @end
    

    viewController里面

      JSContext *context = [[JSContext alloc]init];
        context.exceptionHandler = ^(JSContext *j, JSValue *v){
            NSLog(@"%@",j.exception);
        };
        [context evaluateScript:@"function callback(){return 'hello world'};function setObj(obj){this.obj = obj;obj.value = callback}"];
        [context[@"setObj"] callWithArguments:@[self.testObj]];
    

    调用JS方法,进行赋值,JS对象保留了传进来的obj,最后,JS将自己的回调callback赋值给了obj,方便obj下次回调给JS;由于JS那边保存了obj,而且obj这边也保留了JS的回调。这样就形成了循环引用。
    为了打破这种强引用,apple有一个JSManagedValue 的类,官方的原话:

    The JSManagedValue's JavaScript value is reachable from JavaScript
    
    The owner of the managed reference is reachable in Objective-C. Manually adding or removing the managed reference in the JSVirtualMachine determines reachability.
    

    JSManagedValue 帮助我们保存JSValue,里面保存的JS对象必须在JS中存在,同时 JSManagedValue 的owner在OC中也存在.因此我们把代理的m文件修改如下:

    -(void)setValue:(JSValue *)value{
    //    由于是回掉的关系  obj保存了JS的回掉, js也保存了obj,这样就形成了循环引用
    //    JSManageValue帮助我们保存了JSValue,哪里保存的js对象在js中存在。 JSMangerValue的owner在OC中也存在。
        
        JSManagedValue *mavalue = [JSManagedValue managedValueWithValue:value];
        //建立弱引用关系
        [[[JSContext  currentContext] virtualMachine] addManagedReference:mavalue withOwner:self];
        _value = value;
    }
    
    3.不要在不同的 JSVirtualMachine 之间进行传递JS对象。

    一个JSVirtualMachine可以运行多个context,由于都是在同一个堆内存和同一个垃圾回收下,所以相互之间传值是没问题的。但是如果在不同的 JSVirtualMachine传值,垃圾回收就不知道他们之间的关系了,可能会引起异常。

    4.JavaScriptCore线程是安全的。

    每个context运行的时候通过lock关联的JSVirtualMachine。如果要进行并发操作,可以创建多个JSVirtualMachine实例进行操作。

    相关文章

      网友评论

          本文标题:JavaScriptCore的基本用法(二)

          本文链接:https://www.haomeiwen.com/subject/fvjudttx.html