参考文章
概述
几个重要的类
#import "JSContext.h"
#import "JSValue.h"
#import "JSManagedValue.h"
#import "JSVirtualMachine.h"
#import "JSExport.h"
理解就是, OC 的代码,通过
JSCore
这么个东西,在 OC 和 JS 之间架起一个桥梁.
上图的理解就是,每一个JavaScript上下文(JSContext对象)都归属于一个虚拟机(JSVirtualMachine)。每个虚拟机可以包含多个不同的上下文,并允许在这些不同的上下文之间传值(JSValue对象)。
然而,每个虚拟机都是完整且独立的,有其独立的堆空间和垃圾回收器(garbage collector ),GC无法处理别的虚拟机堆中的对象,因此你不能把一个虚拟机中创建的值传给另一个虚拟机。
本篇笔记的重点是了解下这个调用过程中的并发机制.
JavaScriptCore API都是线程安全的。你可以在任意线程创建JSValue或者执行JS代码.
然而,所有其他想要使用该虚拟机的线程都要等待。
来看下面这段代码来分析
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");
先通过一个图来了解上面的过程.
我们要明确的一点是,线程和虚拟机不存在谁包含谁的概念.我们的并发还是根据 GCD 多线程来理解.只是在某个子队列中执行某个JSContext
时,也许会发生当前的JSContext
所在的虚拟机被占用的情况,导致线程需要等待他执行完以后才能继续.
上面的代码前提为, context 和 context2处于同一个虚拟机, context1是另一个虚拟机.
根据 上面的代码,我们可以理解为(以下按步骤描述)
- 主队列执行 start 打印
- 主队列执行 context 的5s 的休眠.
- 此时并发的子队列 queue 进入了 while 循环,休眠1s 后去执行 context 打印,发现 JS 中的上一步5s 休眠还未执行完毕,因此在此等待.
- 并发的子队列 queue1也 进入了 while 循环,因为是独立的虚拟机,因此可以产生每隔1s 的打印
- 并发的子队列 queue2页进入了 while 循环,此时因为 context2 和 context 处于同一个虚拟机,被 context 占用,因此他这里的打印也不会执行.
因此,最后的打印结果应该是
start
tick_1
tick_1
tick_1
tick_1
end
tick
tick_2