javascript - "window.setTimeout"的 iOS 实现与 JavascriptCore

标签 javascript iphone ios javascriptcore

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

setTimeout(func, period)

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

_JSContext = JSGlobalContextCreate(NULL);

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

这是将具有所需名称的全局 JS 函数映射到静态 objective-c 函数的 native 实现:

- (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);
}

下面是 objective-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, 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 函数:

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

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

JS执行调用原生_setTimeout,5秒后调用原生onTimeoutJSObjectCallAsFunction发生crash。 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

关于javascript - "window.setTimeout"的 iOS 实现与 JavascriptCore,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15991044/

相关文章:

iOS App SQLite 数据库结构改变

javascript - 计算文本区域中文本的大小

javascript - 动态 react 状态

javascript - javascript正则表达式模式中的反向引用

html - 是否可以从基于 Web 的应用程序执行 Objective C 代码

ios - Xcode 6 : iOS 8. 1 开发者磁盘镜像无法挂载

ios - 重复和取消 UILocalNotification

iphone - MKStoreKit -isSubscriptionActive 总是返回 False

javascript - easeljs sprite 使用多个图像进行动画

iphone - 如何从 XML 中获取值