iOS软件开发iOS 进阶

iOS-JavaScriptCore

2018-08-22  本文已影响2人  正谦

JavaScriptCore是Safari的JavaScript引擎,在iOS7之后苹果开放了JavaScriptCore框架,开发者可以通过其提供的OC接口来使用JavaScriptCore。说白了就是它提供了执行JavaScript代码的能力,相当于一个JavaScript的虚拟机。JavaScriptCore是开源的,感兴趣可以研究一下:https://trac.webkit.org/browser/trunk/Source/JavaScriptCore

JavaScriptCore.h中包含了框架中几个比较重要的类:

#import "JSContext.h"         //js上下文,执行js代码
#import "JSValue.h"           //封装js数据类型   
#import "JSManagedValue.h"    //管理JSValue内存
#import "JSVirtualMachine.h"  //JavaScript虚拟机,js底层执行环境
#import "JSExport.h"          //导出OC对象

下面结合几个简单的例子,理解这些对象所扮演的角色:

1.JSContext和JSValue
//demo1.js
1+2*3
//objective-c
- (void)test1
{
    JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
    
    JSContext *ctx = [[JSContext alloc] initWithVirtualMachine:vm];
    
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"demo1" ofType:@"js"];
    NSString *script = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
    
    JSValue *value = [ctx evaluateScript:script];
    
    NSLog(@"%d",[value toInt32]);  //output:7
}

2.访问js对象
//demo2.js
var a = 1+2+3+4+5;
var b = {
    'red':255,
    'blue':0,
    'green':255
};

var c = [b];
//objective-c
- (void)test2
{
    JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
    
    JSContext *ctx = [[JSContext alloc] initWithVirtualMachine:vm];
    
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"demo2" ofType:@"js"];
    NSString *script = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
    
    [ctx evaluateScript:script];
    
    // 访问js对象的三种方式
    NSLog(@"[] %@",ctx[@"a"]);
    NSLog(@"objectForKeyedSubscript %@",[ctx objectForKeyedSubscript:@"a"]);
    NSLog(@"globalObject %@",[ctx.globalObject objectForKeyedSubscript:@"a"]);
    
    // 同样也可以赋值
    ctx[@"a"] = [JSValue valueWithInt32:90 inContext:ctx];
    [ctx setObject:[JSValue valueWithInt32:90 inContext:ctx] forKeyedSubscript:@"a"];
    [ctx.globalObject setObject:[JSValue valueWithInt32:90 inContext:ctx] forKeyedSubscript:@"a"];
    NSLog(@"[] %@",ctx[@"a"]);
    NSLog(@"objectForKeyedSubscript %@",[ctx objectForKeyedSubscript:@"a"]);
    NSLog(@"globalObject %@",[ctx.globalObject objectForKeyedSubscript:@"a"]);
    
    // object对应NSDictionary
    JSValue *b = ctx[@"b"];
    NSLog(@"%@",[b toDictionary]);
    // array对应NSArray
    JSValue *c = ctx[@"c"];
    NSLog(@"%@",[c toArray]);
}

3.block与js function
//demo3.js
var sum = 0;
//由native注入add和myLog方法
for (let i=0;i<=100;i++){ 
    sum = add(sum,i);
}

myLog("sum is "+sum);  //output: sum is 5050
//objective-c
- (void)test3
{
    JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
    
    JSContext *ctx = [[JSContext alloc] initWithVirtualMachine:vm];
    
    ctx[@"myLog"] = ^(NSString *s){
        NSLog(@"myLog:%@",s);
    };
    
    ctx[@"add"] = ^(NSInteger a, NSInteger b){
        return a+b;
    };
    
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"demo3" ofType:@"js"];
    NSString *script = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
    [ctx evaluateScript:script];
}
//JSValue.h
//JSValue本身是一个function,通过callWithArguments调用
- (JSValue *)callWithArguments:(NSArray *)arguments;
//以构造方法执行
- (JSValue *)constructWithArguments:(NSArray *)arguments;
//执行该JSValue对象的某个方法
- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments;

4.JSExport

JSExport提供一种声明式的方法将OC的类和其属性方法导出到JavaScript:
首先要定义一个协议继承自JSExport,在其中声明需要导出的属性和方法,实例类实现该协议并提供相关实现。

//objective-c
//MyView.h
#include <JavaScriptCore/JavaScriptCore.h>

@protocol MyViewExports <JSExport>
- (instancetype)initWithFrame:(CGRect)frame;
- (void)show;
@end

@interface MyView : UIView <MyViewExports>

@property(class,nonatomic,weak) UIViewController    *vc;

@end
//objective-c
//MyView.m
#import "MyView.h"

@implementation MyView
static id _vc = nil;
- (instancetype)initWithFrame:(CGRect)frame;
{
    if (self = [super initWithFrame:frame]) {
        self.backgroundColor = UIColor.redColor;
    }
    return self;
}

- (void)show
{
    [self.class.vc.view addSubview:self];
}

+(void)setVc:(UIViewController *)vc
{
    _vc = vc;
}
+(UIViewController *)vc
{
    return (UIViewController *)_vc;
}

@end

上面代码将一个native自定义的view导出到js,提供了初始化方法和-show方法,我们用一段js代码创建这么一个view放在屏幕上:

//demo4.js
var view = new MyView({x:0,y:0,width:200,height:300});
view.show();

这里有个细节,写过JSPatch应该都知道,像CGRect这种结构体,在js中应该全部展开来写,比如{x:0,y:0,width:200,height:300},而不能写成{origin:{x:0, y:0}, size:{width:200, height:300}}

执行的代码如下:

//objective-c
- (void)test4
{
    MyView.vc = self;
    JSContext *ctx = [[JSContext alloc] init];
    ctx[@"MyView"] = [MyView class];
    
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"demo4" ofType:@"js"];
    NSString *script = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
    [ctx evaluateScript:script];
}

5.内存管理

oc的内存管理是引用计数,而JavaScript则是垃圾回收机制。之前有提到每个JSValue会强引用它的JSContext:

//JSValue.h
@property (readonly, strong) JSContext *context;
上一篇下一篇

猜你喜欢

热点阅读