iOS实现“window.setTimeout”使用JavascriptCore [英] iOS implemention of "window.setTimeout" with JavascriptCore

查看:439
本文介绍了iOS实现“window.setTimeout”使用JavascriptCore的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在iOS应用程序中使用JavaScriptCore库,我正在尝试实现setTimeout函数。

I am using JavaScriptCore library inside iOS application and I am trying to implement setTimeout function.

setTimeout(func, period)

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

After application is launched, the JSC engine with global context is created and two functions are added to that context:

_JSContext = JSGlobalContextCreate(NULL);

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

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

Here is native implementation that is mapping global JS function with desired name to static objective C function:

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

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

And here is the implementation of objective C setTimeout function:

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上调用的函数:

Function that should be called on jsEngine after some delay:

- (void) onTimeout
{
  JSValueRef excp = NULL;
  JSObjectCallAsFunction(timeoutCtx, timeoutFunc, NULL, 0, 0, &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评估的原生函数:

Native function for javascript evaluation:

- (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, NULL, NULL, 0, &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引擎应该对此进行评估:

After that whole setup, the JSC engine should evaluate this:

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

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

The JS execution calls the native _setTimeout, and after five seconds, the native onTimeout is called and crash happens in JSObjectCallAsFunction. The timeoutCtx becomes invalid. Sounds like timeout function context is local and during the time period garbage collector deletes that context in JSC side.

有趣的是,如果 _setTimeout 更改函数是为了立即调用 JSObjectCllAsFunction ,而不等待超时,然后按预期工作。

The interesting thing is also, if _setTimeout function is changed in order to call JSObjectCllAsFunction immediately, without waiting for timeout, then it works as expected.

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

How to prevent automatic context deletion in such asynchronous callbacks?

推荐答案

对于已注册的iOS开发人员,请查看来自wwdc 2013的javascript核心新视频,名为将JavaScript集成到本机应用程序。你会发现最新的iOS版本的解决方案。

For registered iOS developer, take a look at the new video about javascript core from wwdc 2013 called "Integrating JavaScript into Native Apps". You will find there the solution for newest iOS version.

对于当前的iOS版本,我的替代解决方案是在全局数组 JSC用于存储应该受到垃圾收集器保护的对象。因此,您可以控制在不再需要时从数组中弹出变量。

My alternative solution, for the current iOS version, was to make a global array in JSC for storing objects that should be protected from garbage collector. So, you have control to pop variable from array when it is not needed any more.

这篇关于iOS实现“window.setTimeout”使用JavascriptCore的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆