WKWebView:尝试从主线程同步查询javascript

时间:2015-02-07 22:26:47

标签: ios ios8 grand-central-dispatch semaphore wkwebview

有没有办法从主线程同步查询javascript?

使用具有回调参数的异步函数从本机代码查询Javascript以处理响应:

func evaluateJavaScript(_ javaScriptString: String, completionHandler completionHandler: ((AnyObject!, NSError!) -> Void)?)

异步行为通常可以通过暂停线程和放大器来实现同步。使用信号量控制执行:

// Executing in the main thread
let sema = dispatch_semaphore_create(0)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
// Background thread
  self.evaluateJavaScript("navigator.userAgent", completionHandler: { (value:AnyObject!, error: NSError!) -> Void in
    if let ua = value as? String {
      userAgent = ua
    } else {
      ERROR("ERROR There was an error retrieving the default user agent, using hardcoded value \(error)")
    }
    dispatch_semaphore_signal(sema)
  })
}
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)

...但是在这种情况下,因为completionHandler 总是在主线程上调用,所以代码死锁,因为completionHandler块永远不会执行(主线程)暂停了最后一行的dispatch_semaphore_wait

有什么建议吗?

修改

我宁愿不阻止主线程执行该代码。但是我不能在不将我的API从同步变为异步的情况下从主线程中解耦,在堆栈中一直有多米诺骨牌效应(例如从let ua = computeUserAgent()computeUserAgent() {(ua: String)->Void in /*Use ua value here */})。因此,我需要选择两种方法都有缺点,我宁愿选择不会弄乱我的内部API的方法,特别是对于像查找用户代理这样简单的任务。

1 个答案:

答案 0 :(得分:2)

如果你必须这样做......

正如对this answer的评论中所建议的那样,你可以围绕你的信号量运行一个紧密的循环。等等。

while (dispatch_semaphore_wait(sema, DISPATCH_TIME_NOW)) { 
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 
                             beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}