【发布时间】:2013-04-13 17:58:42
【问题描述】:
我在 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), 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 评估的本机函数:
- (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秒后调用原生onTimeout,在JSObjectCallAsFunction发生crash。 timeoutCtx 无效。听起来超时函数上下文是本地的,并且在此期间垃圾收集器会在 JSC 端删除该上下文。
有趣的是,如果更改_setTimeout 函数以便立即调用JSObjectCllAsFunction,而不等待超时,那么它会按预期工作。
如何防止在这种异步回调中自动删除上下文?
【问题讨论】:
-
你能解决这个问题吗?我也面临 setTimeOut() 函数的类似问题。
-
如果您是注册 iOS 开发者,请观看 wwdc 2013 中有关 javascript 核心的视频。您将在那里找到解决此问题的方法。
-
是的,Prcela,我已经看过了,但那是针对 iOS 7 的。我会尝试为第三方 JSCore 库提供 iOS 5 和 iOS 6 解决方案的答案。谢谢... +1。
-
嗨 Prcela,你从哪里获得 JSEngine 类?我正在使用phoboslab.org/log/2011/06/javascriptcore-project-files-for-ios 提供的 JSCore 库。我必须使用它,因为我必须支持 iOS 5 和 iOS 6 设备。你能指导我吗??
标签: javascript iphone ios javascriptcore