iOS使用JavascriptCore实现“window.setTimeout”

我在iOS应用程序中使用 JavaScriptCore库,我正在尝试实现setTimeout函数.
setTimeout(func,period)

启动应用程序后,将创建具有全局上下文的JSC引擎,并向该上下文添加两个函数:

_JSContext = JSGlobalContextCreate(NULL);

[self mapName:"iosSetTimeout" toFunction:_setTimeout];
[self mapName:"iosLog" toFunction:_log];

这是本机实现,它将具有所需名称的全局JS函数映射到静态目标C函数:

- (void) mapName:(const char*)name toFunction:(JSObjectCallAsFunctionCallback)func
{
  JSStringRef nameRef = JSStringCreateWithUTF8CString(name);
  JSObjectRef funcRef = JSObjectMakeFunctionWithCallback(_JSContext,nameRef,func);
  JSObjectSetProperty(_JSContext,JSContextGetGlobalObject(_JSContext),funcRef,kJSPropertyAttributeNone,NULL);
  JSStringRelease(nameRef);
}

这里是目标C setTimeout函数的实现:

JSValueRef _setTimeout(JSContextRef ctx,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef* exception)
{
  if(argumentCount == 2)
  {
    JSEngine *jsEngine = [JSEngine shared];
    jsEngine.timeoutCtx =  ctx;
    jsEngine.timeoutFunc = (JSObjectRef)arguments[0];
    [jsEngine performSelector:@selector(onTimeout) withObject:nil afterDelay:5];
  }
  return JSValueMakeNull(ctx);
}

一段延迟后应该在jsEngine上调用的函数:

- (void) onTimeout
{
  JSValueRef excp = NULL;
  JSObjectCallAsFunction(timeoutCtx,timeoutFunc,NULL,&excp);
  if (excp) {
    JSStringRef exceptionArg = JSValueToStringCopy([self JSContext],excp,NULL);
    NSString* exceptionRes = (__bridge_transfer NSString*)JSStringCopyCFString(kCFAllocatorDefault,exceptionArg);  
    JSStringRelease(exceptionArg);
    NSLog(@"[JSC] JavaScript exception: %@",exceptionRes);
  }
}

用于javascript评估的本机函数:

- (NSString *)evaluate:(NSString *)script
{
    if (!script) {
        NSLog(@"[JSC] JS String is empty!");
        return nil;
    }


    JSStringRef scriptJS = JSStringCreateWithUTF8CString([script UTF8String]);
    JSValueRef exception = NULL;

    JSValueRef result = JSEvaluateScript([self JSContext],scriptJS,&exception);
    NSString *res = nil;

    if (!result) {
        if (exception) {
            JSStringRef exceptionArg = JSValueToStringCopy([self JSContext],exception,NULL);
            NSString* exceptionRes = (__bridge_transfer NSString*)JSStringCopyCFString(kCFAllocatorDefault,exceptionArg);

            JSStringRelease(exceptionArg);
            NSLog(@"[JSC] JavaScript exception: %@",exceptionRes);
        }

        NSLog(@"[JSC] No result returned");
    } else {
        JSStringRef jstrArg = JSValueToStringCopy([self JSContext],result,NULL);
        res = (__bridge_transfer NSString*)JSStringCopyCFString(kCFAllocatorDefault,jstrArg);

        JSStringRelease(jstrArg);
    }

    JSStringRelease(scriptJS);

    return res;
}

在整个设置之后,JSC引擎应该评估这个:

[jsEngine evaluate:@"iosSetTimeout(function(){iosLog('timeout done')},5000)"];

JS执行调用本机_setTimeout,五秒后,调用本机onTimeout并在JSObjectCallAsFunction中发生崩溃. timeoutCtx变为无效.听起来像超时功能上下文是本地的,在此期间垃圾收集器删除JSC端的上下文.

有趣的是,如果更改_setTimeout函数以便立即调用JSObjectCllAsFunction,而不等待超时,那么它将按预期工作.

如何防止这种异步回调中的自动上下文删除?

解决方法

我最终将setTimeout添加到这样的特定JavaScriptCore上下文中,并且它运行良好:
JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
JSContext *context = [[JSContext alloc] initWithVirtualMachine: vm];

// Add setTimout
context[@"setTimeout"] = ^(JSValue* function,JSValue* timeout) {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)([timeout toInt32] * NSEC_PER_MSEC)),dispatch_get_main_queue(),^{
        [function callWithArguments:@[]];
    });
};

在我的例子中,这允许我在JavaScriptCore中使用cljs.core.async / timeout.

以上是来客网为你收集整理的iOS使用JavascriptCore实现“window.setTimeout”全部内容,希望文章能够帮你解决iOS使用JavascriptCore实现“window.setTimeout”所遇到的程序开发问题。

如果觉得来客网网站内容还不错,欢迎将来客网网站推荐给程序员好友。