Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
iOS的实施;window.setTimeout";使用JavascriptCore_Javascript_Iphone_Ios_Javascriptcore - Fatal编程技术网

iOS的实施;window.setTimeout";使用JavascriptCore

iOS的实施;window.setTimeout";使用JavascriptCore,javascript,iphone,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];

我在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);
}
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);
}
下面是objective C setTimeout函数的实现:

- (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);
}
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执行调用本机
\u setTimeout
,五秒钟后,调用本机
onTimeout
,并在
JSObjectCallAsFunction
中发生崩溃。
timeoutCtx
无效。听起来超时函数上下文是本地的,在此期间,垃圾收集器会在JSC端删除该上下文

有趣的是,如果
\u setTimeout
函数被更改,以便在不等待超时的情况下立即调用
JSObjectCllAsFunction
,那么它将按预期工作

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

这是我的两分钱

我认为没有必要在
\u setTimeout
中保留对上下文的引用。以后可以利用全局上下文调用计时器函数

您应该使用
JSValueProtect
\u setTimeout
中从GC保护
jsEngine.timeoutpunc
。否则,它可能会变成无效引用,并在以后导致崩溃。

不要挂起JSContextRefs,使用JSGlobalContextCreate创建的JSContextRefs除外 具体来说,这是不好的:

jsEngine.timeoutCtx =  ctx;
....
JSObjectCallAsFunction(timeoutCtx
将全局上下文传递给JSObjectCallAsFunction,而不是保存ctx

您必须保护任何要保留的值,使其比回调时间长 JavaScriptCore垃圾收集器可以在调用JavaScriptCore函数的任何时候运行。在您的示例中,作为setTimeout的单个参数创建的匿名函数没有被JavaScript中的任何内容引用,这意味着它可以在调用setTimeout完成后的任何时间点被垃圾收集。因此,setTimeout必须保护函数,告诉JavaScriptCore不要收集它。在使用JSObjectCallAsFunction调用函数之后,您应该将其JSValueUnprotect,否则该匿名函数将在内存中挂起,直到全局上下文被销毁

另外:如果不想从函数返回任何内容,通常应该返回JSValueMakeUndefined(ctx)
JSValueMakeNull(ctx)与未定义的不同。我的一般规则是,如果在Objective-C中返回void,则返回JSValueMakeUndefined;如果返回nil对象,则返回JSValueMakeNull。但是,如果您想像窗口对象一样实现setTimeout,那么它需要返回一个ID/句柄,该ID/句柄可以传递给clearTimeout以取消计时器。

对于注册的iOS开发人员,请看一看wwdc 2013中名为的关于javascript核心的新视频。您将在那里找到最新iOS版本的解决方案


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

我最终将
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

基于@ninjudd的回答,我在swift中就是这么做的

    let setTimeout: @objc_block (JSValue, Int) -> Void = {
        [weak self] (cb, wait) in

        let callback = cb as JSValue

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(UInt64(wait) * NSEC_PER_MSEC)), dispatch_get_main_queue(), { () -> Void in
            callback.callWithArguments([])
        })
    }
    context.setObject(unsafeBitCast(setTimeout, AnyObject.self), forKeyedSubscript: "setTimeout")

我已经在Swift上实现了
settimeout
setInterval
cleartimout
来解决这个问题。通常,示例仅显示
setTimeout
函数,而不显示使用
cleartimout
的选项。如果您使用的是JS依赖项,那么您很可能还需要
clearTimeout
setInterval
函数

import Foundation
import JavaScriptCore

let timerJSSharedInstance = TimerJS()

@objc protocol TimerJSExport : JSExport {

    func setTimeout(_ callback : JSValue,_ ms : Double) -> String

    func clearTimeout(_ identifier: String)

    func setInterval(_ callback : JSValue,_ ms : Double) -> String

}

// Custom class must inherit from `NSObject`
@objc class TimerJS: NSObject, TimerJSExport {
    var timers = [String: Timer]()

    static func registerInto(jsContext: JSContext, forKeyedSubscript: String = "timerJS") {
        jsContext.setObject(timerJSSharedInstance,
                            forKeyedSubscript: forKeyedSubscript as (NSCopying & NSObjectProtocol))
        jsContext.evaluateScript(
            "function setTimeout(callback, ms) {" +
            "    return timerJS.setTimeout(callback, ms)" +
            "}" +
            "function clearTimeout(indentifier) {" +
            "    timerJS.clearTimeout(indentifier)" +
            "}" +
            "function setInterval(callback, ms) {" +
            "    return timerJS.setInterval(callback, ms)" +
            "}"
        )       
    }

    func clearTimeout(_ identifier: String) {
        let timer = timers.removeValue(forKey: identifier)

        timer?.invalidate()
    }


    func setInterval(_ callback: JSValue,_ ms: Double) -> String {
        return createTimer(callback: callback, ms: ms, repeats: true)
    }

    func setTimeout(_ callback: JSValue, _ ms: Double) -> String {
        return createTimer(callback: callback, ms: ms , repeats: false)
    }

    func createTimer(callback: JSValue, ms: Double, repeats : Bool) -> String {
        let timeInterval  = ms/1000.0

        let uuid = NSUUID().uuidString

        // make sure that we are queueing it all in the same executable queue...
        // JS calls are getting lost if the queue is not specified... that's what we believe... ;)
        DispatchQueue.main.async(execute: {
            let timer = Timer.scheduledTimer(timeInterval: timeInterval,
                                             target: self,
                                             selector: #selector(self.callJsCallback),
                                             userInfo: callback,
                                             repeats: repeats)
            self.timers[uuid] = timer
        })


        return uuid
    }
用法示例:

jsContext = JSContext()
TimerJS.registerInto(jsContext: jsContext)

我希望这能有所帮助

你能解决这个问题吗??对于setTimeOut()函数,我也面临类似的问题。如果您是注册的iOS开发者,请看一下wwdc 2013中有关javascript核心的视频。你会发现这个问题的解决方案。是的,我已经看过了,但那是iOS 7的。我将为第三方JSCore库的iOS 5和iOS 6解决方案尝试您的答案。谢谢+1.你好,普赛拉,你从哪里获得JSEngine等级??我正在使用由提供的JSCore库。我必须使用它,因为我必须支持iOS 5和iOS 6设备。你能为我做些指导吗?这是一个对内部结构的极好和有趣的洞察。。。thxMissing
callJsCallback
:func callJsCallback(timer:timer){let callback=(timer.userInfo as!JSValue)callback.call(withArguments:nil)}链接已断开