我目前正在尝试在本地代码的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
以下是我的视图控制器的简化版本:
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")
}
}
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])
}
}
executeBackground
和executeCallback
方法是辅助函数,executeCallback
正在使用setTimeout
来回应我在此SO帖子/答案中所读到的内容:{{ 3}}它似乎解决了与锁定相关的其他崩溃。
当我将executeBackground
替换为主线程上运行的以下函数时,我无法复制崩溃:
private func executeMain(callback: JSValue!, f: ()->([String:AnyObject])) {
dispatch_async(dispatch_get_main_queue()) {
self.executeCallback(callback, result: f())
}
}
在将后台线程中创建的数据传递到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)
})
})
}
虽然我无法证明已经修复了崩溃,但我们已经对此功能进行了相当广泛的测试,并且没有再次看到它。我没有发布这个答案来解决我自己的问题的原因是我不知道为什么确切地说这是不同的和/或更好的。如果有人了解NSOperationQueues的变化可能已经解决了什么,那将非常感激!