В настоящее время я пытаюсь отладить сбои в реализации JavascriptCore интерфейса для собственного кода, чтобы выполнить некоторую работу от имени кода javascript в WebView.
Иногда сбой происходит очень скоро после запуска приложения, в других случаях для его возникновения может потребоваться несколько минут выполнения сотен вызовов собственного кода.
Это две верхние строки обратной трассировки каждого сбоя:
(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: JavaScriptCore -- Передача функции в качестве параметра в ObjC, похоже, устранила другие сбои, связанные с блокировкой.
Когда я заменил executeBackground
на следующую функцию, которая выполняется только в основном потоке, я не смог воспроизвести сбой:
private func executeMain(callback: JSValue!, f: ()->([String:AnyObject])) {
dispatch_async(dispatch_get_main_queue()) {
self.executeCallback(callback, result: f())
}
}
Кажется, что есть какая-то проблема, возникающая при передаче данных, созданных в фоновом потоке, в WebView, но после просмотра документации я не уверен, что может быть. Единственное табу, о котором я упоминал, это передача данных между несколькими экземплярами JSVirtualMachine, что не кажется применимым, поскольку я взаимодействую только с одним экземпляром WebView. Любая помощь в выяснении этого очень ценится!
Обновлять
Кажется, я решил проблему, отключив использование Grand Central Dispatch непосредственно для NSOperationQueues. После изменения 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, это было бы очень признательно!
dispatch_sync(dispatch_get_main_queue())
в функцииexecuteBackground
. Должно бытьdispatch_async(dispatch_get_main_queue())
- person KrishnaCA   schedule 22.11.2016