在JavaScriptCore.h中,我们可以看到这个
#ifndef JavaScriptCore_h #define JavaScriptCore_h #include <JavaScriptCore/JavaScript.h> #include <JavaScriptCore/JSStringRefCF.h> #if defined(__OBJC__) && JSC_OBJC_API_ENABLED #import "JSContext.h" #import "JSValue.h" #import "JSManagedValue.h" #import "JSVirtualMachine.h" #import "JSExport.h" #endif #endif /* JavaScriptCore_h */这里已经很清晰地列出了JavaScriptCore的主要几个类:
JSContext
JSValue
JSManagedValue
JSVirtualMachine
JSExport
接下来我们会依次讲解这几个类的用法。
6. Hello World!这段代码展示了如何在Objective-C中执行一段JavaScript代码,并且获取返回值并转换成OC数据打印
//创建虚拟机 JSVirtualMachine *vm = [[JSVirtualMachine alloc] init]; //创建上下文 JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm]; //执行JavaScript代码并获取返回值 JSValue *value = [context evaluateScript:@"1+2*3"]; //转换成OC数据并打印 NSLog(@"value = %d", [value toInt32]); Output value = 7 三、 JSVirtualMachine一个JSVirtualMachine的实例就是一个完整独立的JavaScript的执行环境,为JavaScript的执行提供底层资源。
这个类主要用来做两件事情:
实现并发的JavaScript执行
JavaScript和Objective-C桥接对象的内存管理
看下头文件SVirtualMachine.h里有什么:
NS_CLASS_AVAILABLE(10_9, 7_0) @interface JSVirtualMachine : NSObject /* 创建一个新的完全独立的虚拟机 */ (instancetype)init; /* 对桥接对象进行内存管理 */ - (void)addManagedReference:(id)object withOwner:(id)owner; /* 取消对桥接对象的内存管理 */ - (void)removeManagedReference:(id)object withOwner:(id)owner; @end每一个JavaScript上下文(JSContext对象)都归属于一个虚拟机(JSVirtualMachine)。每个虚拟机可以包含多个不同的上下文,并允许在这些不同的上下文之间传值(JSValue对象)。
然而,每个虚拟机都是完整且独立的,有其独立的堆空间和垃圾回收器(garbage collector ),GC无法处理别的虚拟机堆中的对象,因此你不能把一个虚拟机中创建的值传给另一个虚拟机。
线程和JavaScript的并发执行
JavaScriptCore API都是线程安全的。你可以在任意线程创建JSValue或者执行JS代码,然而,所有其他想要使用该虚拟机的线程都要等待。
如果想并发执行JS,需要使用多个不同的虚拟机来实现。
可以在子线程中执行JS代码。
通过下面这个demo来理解一下这个并发机制
JSContext *context = [[CustomJSContext alloc] init]; JSContext *context1 = [[CustomJSContext alloc] init]; JSContext *context2 = [[CustomJSContext alloc] initWithVirtualMachine:[context virtualMachine]]; NSLog(@"start"); dispatch_async(queue, ^{ while (true) { sleep(1); [context evaluateScript:@"log('tick')"]; } }); dispatch_async(queue1, ^{ while (true) { sleep(1); [context1 evaluateScript:@"log('tick_1')"]; } }); dispatch_async(queue2, ^{ while (true) { sleep(1); [context2 evaluateScript:@"log('tick_2')"]; } }); [context evaluateScript:@"sleep(5)"]; NSLog(@"end");context和context2属于同一个虚拟机。
context1属于另一个虚拟机。
三个线程分别异步执行每秒1次的js log,首先会休眠1秒。
在context上执行一个休眠5秒的JS函数。
首先执行的应该是休眠5秒的JS函数,在此期间,context所处的虚拟机上的其他调用都会处于等待状态,因此tick和tick_2在前5秒都不会有执行。
而context1所处的虚拟机仍然可以正常执行tick_1。
休眠5秒结束后,tick和tick_2才会开始执行(不保证先后顺序)。
实际运行输出的log是:
start tick_1 tick_1 tick_1 tick_1 end tick tick_2 四、 JSContext一个JSContext对象代表一个JavaScript执行环境。在native代码中,使用JSContext去执行JS代码,访问JS中定义或者计算的值,并使JavaScript可以访问native的对象、方法、函数。
1. JSContext执行JS代码调用evaluateScript函数可以执行一段top-level 的JS代码,并可向global对象添加函数和对象定义
其返回值是JavaScript代码中最后一个生成的值
API Reference
NS_CLASS_AVAILABLE(10_9, 7_0) @interface JSContext : NSObject /* 创建一个JSContext,同时会创建一个新的JSVirtualMachine */ (instancetype)init; /* 在指定虚拟机上创建一个JSContext */ (instancetype)initWithVirtualMachine: (JSVirtualMachine*)virtualMachine; /* 执行一段JS代码,返回最后生成的一个值 */ (JSValue *)evaluateScript:(NSString *)script; /* 执行一段JS代码,并将sourceURL认作其源码URL(仅作标记用) */ - (JSValue *)evaluateScript:(NSString *)script withSourceURL:(NSURL*)sourceURL NS_AVAILABLE(10_10, 8_0); /* 获取当前执行的JavaScript代码的context */ + (JSContext *)currentContext; /* 获取当前执行的JavaScript function*/ + (JSValue *)currentCallee NS_AVAILABLE(10_10, 8_0); /* 获取当前执行的JavaScript代码的this */ + (JSValue *)currentThis; /* Returns the arguments to the current native callback from JavaScript code.*/ + (NSArray *)currentArguments; /* 获取当前context的全局对象。WebKit中的context返回的便是WindowProxy对象*/ @property (readonly, strong) JSValue *globalObject; @property (strong) JSValue *exception; @property (copy) void(^exceptionHandler)(JSContext *context, JSValue *exception); @property (readonly, strong) JSVirtualMachine *virtualMachine; @property (copy) NSString *name NS_AVAILABLE(10_10, 8_0); @end 2. JSContext访问JS对象