使用后台线程中的数据执行回调后,JavacriptCore WebKit EXC_BAD_ACCESS崩溃

Aar*_*ron 8 cocoa webkit grand-central-dispatch javascriptcore swift

我目前正在尝试在JavascriptCore接口的JavascriptCore实现中调试崩溃,以便本机代码代表WebView中的javascript代码执行一些工作.

崩溃有时会在启动应用程序后很快发生,有时可能需要几分钟才能执行数百次对本机代码的调用.

这些是每次崩溃回溯的前两行:

(lldb) thread backtrace
* thread #1: tid = 0x37960c, 0x00007fff8de6ecca JavaScriptCore`sanitizeStackForVMImpl + 15, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x700001a2c000)
    frame #0: 0x00007fff8de6ecca JavaScriptCore`sanitizeStackForVMImpl + 15
Run Code Online (Sandbox Code Playgroud)

这是我的视图控制器的简化版本:

class MyViewController: NSViewController, WebFrameLoadDelegate {

    let worker = Worker() 

    // other setup code...

    func webView(webView: WebView!, didCreateJavaScriptContext context: JSContext!, forFrame frame: WebFrame!) {
        context.setObject(worker, forKeyedSubscript: "ClientWorker")
    }
}
Run Code Online (Sandbox Code Playgroud)

JSExport协议本身,以及执行代码的实现工作.为了测试,我删除了实际的工作,只返回一个带有虚拟数据的字典,崩溃仍然发生.

@objc protocol WorkerJSExports: JSExport {
    func doWork(params: [String:AnyObject], callback: JSValue)
}

@objc class Worker: NSObject, WorkerJSExports {

    func doWork(params: [String:AnyObject], callback: JSValue) {
        executeBackground(callback) {
            return [
                "test": "data"
            ]
        }
    }

    private func executeBackground(callback: JSValue!, f: ()->([String:AnyObject])) {
        dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)) {
            let result = f()
            dispatch_sync(dispatch_get_main_queue()) {
                self.executeCallback(callback, result: result)
            }
        }
    }

    private func executeCallback(callback: JSValue!, result: AnyObject) {
        callback.context.evaluateScript("setTimeout").callWithArguments([callback, 0, result])
    }
}
Run Code Online (Sandbox Code Playgroud)

executeBackgroundexecuteCallback方法是辅助功能,并且executeCallback正在使用的setTimeout响应我在这个岗位SO /复读:JavaScriptCore的-传递一个函数作为参数传递给ObjC,它似乎已经解决了与锁有关的其他崩溃.

当我换出executeBackground以下在主线程上运行的函数时,我无法复制崩溃:

private func executeMain(callback: JSValue!, f: ()->([String:AnyObject])) {
    dispatch_async(dispatch_get_main_queue()) {
        self.executeCallback(callback, result: f())
    }
}
Run Code Online (Sandbox Code Playgroud)

似乎在将后台线程中创建的数据传递到WebView时会出现某种问题,但在完成文档之后,我不确定可能是什么.我发现的唯一禁忌是在多个JSVirtualMachine实例之间传递数据,这似乎不适用,因为我只与一个WebView实例进行交互.任何帮助搞清楚这一点非常感谢!


更新

我似乎已经通过直接为NSOperationQueues转换使用Grand Central Dispatch解决了这个问题.更改executeBackground为以下后,崩溃没有再次发生.

private let workerQueue = NSOperationQueue()

private func executeAsync(callback: JSValue!, f: ()->([String:AnyObject])) {
    self.workerQueue.addOperationWithBlock({
        let result = f()
        NSOperationQueue.mainQueue().addOperationWithBlock({
            self.executeCallback(callback, result: result)
        })
    })
}
Run Code Online (Sandbox Code Playgroud)

虽然我无法真正证明这已经解决了崩溃问题,但我们已经对此功能进行了相当广泛的测试,并且没有再次看到它.我之所以没有对自己的问题发表回答的原因是我不知道为什么这个与众不同和/或更好.如果有人了解NSOperationQueues的变化可能已经解决了什么,那将非常感谢!