iOS使用JavascriptCore实现“window.setTimeout”

前端之家收集整理的这篇文章主要介绍了iOS使用JavascriptCore实现“window.setTimeout”前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我在iOS应用程序中使用 JavaScriptCore库,我正在尝试实现setTimeout函数.
  1. setTimeout(func,period)

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

  1. _JSContext = JSGlobalContextCreate(NULL);
  2.  
  3. [self mapName:"iosSetTimeout" toFunction:_setTimeout];
  4. [self mapName:"iosLog" toFunction:_log];

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

  1. - (void) mapName:(const char*)name toFunction:(JSObjectCallAsFunctionCallback)func
  2. {
  3. JSStringRef nameRef = JSStringCreateWithUTF8CString(name);
  4. JSObjectRef funcRef = JSObjectMakeFunctionWithCallback(_JSContext,nameRef,func);
  5. JSObjectSetProperty(_JSContext,JSContextGetGlobalObject(_JSContext),funcRef,kJSPropertyAttributeNone,NULL);
  6. JSStringRelease(nameRef);
  7. }

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

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

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

  1. - (void) onTimeout
  2. {
  3. JSValueRef excp = NULL;
  4. JSObjectCallAsFunction(timeoutCtx,timeoutFunc,NULL,&excp);
  5. if (excp) {
  6. JSStringRef exceptionArg = JSValueToStringCopy([self JSContext],excp,NULL);
  7. NSString* exceptionRes = (__bridge_transfer NSString*)JSStringCopyCFString(kcfAllocatorDefault,exceptionArg);
  8. JSStringRelease(exceptionArg);
  9. NSLog(@"[JSC] JavaScript exception: %@",exceptionRes);
  10. }
  11. }

用于javascript评估的本机函数

  1. - (NSString *)evaluate:(NSString *)script
  2. {
  3. if (!script) {
  4. NSLog(@"[JSC] JS String is empty!");
  5. return nil;
  6. }
  7.  
  8.  
  9. JSStringRef scriptJS = JSStringCreateWithUTF8CString([script UTF8String]);
  10. JSValueRef exception = NULL;
  11.  
  12. JSValueRef result = JSEvaluateScript([self JSContext],scriptJS,&exception);
  13. NSString *res = nil;
  14.  
  15. if (!result) {
  16. if (exception) {
  17. JSStringRef exceptionArg = JSValueToStringCopy([self JSContext],exception,NULL);
  18. NSString* exceptionRes = (__bridge_transfer NSString*)JSStringCopyCFString(kcfAllocatorDefault,exceptionArg);
  19.  
  20. JSStringRelease(exceptionArg);
  21. NSLog(@"[JSC] JavaScript exception: %@",exceptionRes);
  22. }
  23.  
  24. NSLog(@"[JSC] No result returned");
  25. } else {
  26. JSStringRef jstrArg = JSValueToStringCopy([self JSContext],result,NULL);
  27. res = (__bridge_transfer NSString*)JSStringCopyCFString(kcfAllocatorDefault,jstrArg);
  28.  
  29. JSStringRelease(jstrArg);
  30. }
  31.  
  32. JSStringRelease(scriptJS);
  33.  
  34. return res;
  35. }

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

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

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

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

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

解决方法

我最终将setTimeout添加到这样的特定JavaScriptCore上下文中,并且它运行良好:
  1. JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
  2. JSContext *context = [[JSContext alloc] initWithVirtualMachine: vm];
  3.  
  4. // Add setTimout
  5. context[@"setTimeout"] = ^(JSValue* function,JSValue* timeout) {
  6. dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)([timeout toInt32] * NSEC_PER_MSEC)),dispatch_get_main_queue(),^{
  7. [function callWithArguments:@[]];
  8. });
  9. };

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

猜你在找的iOS相关文章