GCD和webView死锁
我发现了一个似乎在WebKit导致死锁的问题。 如果我从我的主线程运行这个代码,我正确地看到一个警报。 我可以点击警报上的“确定”button,它被解散,一切工作正常:
[theWebView stringByEvaluatingJavaScriptFromString:@"alert('hi');"];
如果稍作修改,则警告消息仍会出现,但不能点击确定button – 您不能closures警报,并且如果进入应用程序,则会挂起在stringByEvaluatingJavaScriptFromString
调用中:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_main_queue(), ^{ [theWebView stringByEvaluatingJavaScriptFromString:@"alert('hi');"]; }); });
这两者唯一不同的是,第二个是在调度队列的上下文中的主线程中运行JS。
另一方面,如果我做了以下,那么挂起不会发生:
- (void) showHi:(id) it { [(UIWebView*)it stringByEvaluatingJavaScriptFromString:@"alert('hi');"]; } .... dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self performSelectorOnMainThread:@selector(showHi:) withObject:theWebView waitUntilDone:NO]; });
有人能指出什么是错误的,导致挂起?
编辑:
相关问题:
使用dispatch_async或performSelectorOnMainThread在主线程上执行UI更改?
主队列上的performSelectorOnMainThread和dispatch_async有什么区别?
Grand Central Dispatch(GCD)与performSelector – 需要更好的解释
非常类似的问题:
使用GCD调用时,UIWebView stringByEvaluatingJavaScriptFromString在iOS5.0 / 5.1上挂起
这似乎只是UIWebView中的一个错误。 根据这个问题 ,它是在iOS 5中引入的,并且在iOS 4.3和更低版本中没有发生死锁。
有趣的是,在调用stringByEvaluatingJavaScriptFromString:
之前提交一个UIAlertView stringByEvaluatingJavaScriptFromString:
奇怪地防止了死锁:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_main_queue(), ^{ UIAlertView *message = [[UIAlertView alloc] initWithTitle:@"Test" message:@"Test" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [message show]; [theWebView stringByEvaluatingJavaScriptFromString:@"alert('hi');"]; }); });
但是,我的理论是这样的:当我在死锁发生后暂停执行时,我看到WebThread在__psynch_mutexwait
处被暂停。 由于JavaScript引擎是在不同的线程上执行的,因此必须告诉主线程显示alert视图。 但是, stringByEvaluatingJavaScriptFromString:
是一个返回值的阻塞调用。 只有通过单击确定解除警报后才能返回该值。 这是看似发生死锁的地方:从另一个线程中,我们告诉主线程告诉web视图运行JavaScript(这发生在另一个线程上),这反过来告诉主线程显示一个alert view一旦OK单击,将其返回值传回JavaScript。 只有当调用stringByEvaluatingJavaScriptFromString:
返回时,我们传递给GCD的块才完成。
不过这肯定是个bug。 奇怪的是,当我首先显示一个UIAlertView时,死锁不会发生。 也许iOS在这种情况下将第二个警报视图放在某种types的队列中,这样可以防止死锁。 奇!
我认为这是在webview中陈述 类参考
现在,对于你死锁的情况,你可以通过replace[self performSelectorOnMainThread:@selector(showHi:) withObject:theWebView waitUntilDone:NO];
来模拟相同的情况[self performSelectorOnMainThread:@selector(showHi:) withObject:theWebView waitUntilDone:NO];
同
performSelector:withObject:afterDelay:inModes:
在默认情况下,它是NSDefaultRunLoopMode
,它本质上是primefaces的,而在dispatch_get_main_queue()
非primefaces情况下,你必须改变线程的当前调度模式。
我希望它保持翔实。 此外,欢迎提供更多build议。
有几件事情可能是这方面的一个因素。 正如其他人所提到的,JavaScript并不在主线程上执行,而是在自己的后台线程上执行。 您可能已经注意到,代码的执行一直等到JavaScript完成。 我不知道代码中的具体实现,但它听起来像是在内部使用dispatch_sync
或dispatch_barrier_sync
。
下一个您应该注意的事项是从UIAlertView -show
和JavaScript alert(..)
之间存在显着差异。 UIAlertView -show
asynchronous操作。 您可以这样说,因为在解除警报之前,在调用之后代码执行会继续。 如果您在JavaScript中执行相同的实验,代码执行将停止,直到警报closures。
最后,调度队列和线程之间是有区别的。 你可以阅读更多关于它的线程 。 我怀疑JavaScript执行正在执行[NSThread isMainThread]
,它报告NO
因为它在主队列,而不是主线程。 由于此检查报告为“ NO
,因此它对主队列执行dispatch_sync
,而主队列已经被阻塞,等待JavaScript队列。
- 全局队列中的定时器不在iOS中调用
- 主线程在viewDidLoad中的一个并发队列上执行dispatch_async,或者在一个方法内部执行
- 如果我嵌套dispatch_async调用会发生什么?
- 在iOS上,你可以做一个同步networking请求(但不是在主线程),仍然进展callback(在一个单独的,非主线程)?
- 在应用程序委托中返回之前,等待asynchronous任务完成完成块
- 如何包装一个asynchronous方法,它将一个块转换为同步的目标c
- 有一个GCD队列的队列字典
- 核心数据和GCD:将正确的托pipe对象上下文传递给自定义NSManagedObjects
- 是否有必要在GCD下创build一个autorelease池?