我发现了一个似乎导致WebKit陷入僵局的问题。如果我从我的主线程运行此代码,我正确地看到一个警报。我可以点击" OK"警报上的按钮,它解散,一切运作良好:
[theWebView stringByEvaluatingJavaScriptFromString:@"alert('hi');"];
如果我做了一些修改,那么警报信息仍会显示,但是无法点按“确定”按钮 - 您无法关闭警报,如果您闯入应用程序,则会挂起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];
});
有人可以对导致悬挂的问题有所了解吗?
编辑:
相关问题:
Perform UI Changes on main thread using dispatch_async or performSelectorOnMainThread?
Whats the difference between performSelectorOnMainThread and dispatch_async on main queue?
Grand Central Dispatch (GCD) vs. performSelector - need a better explanation
非常相似的问题:
UIWebView stringByEvaluatingJavaScriptFromString hangs on iOS5.0/5.1 when called using GCD
答案 0 :(得分:5)
我认为已在webview 类引用
中说明了这一点现在,对于死锁的情况,您可以通过替换它来模拟死锁
[self performSelectorOnMainThread:@selector(showHi:) withObject:theWebView waitUntilDone:NO];
与
performSelector:withObject:afterDelay:inModes:
。
默认情况下,使用“分别”模式为NSDefaultRunLoopMode
,这本质上是原子的,而在dispatch_get_main_queue()
的非原子情况下,您必须更改当前的线程分派模式。
我希望它仍然有用。此外,欢迎提出更多建议。
答案 1 :(得分:4)
这似乎只是UIWebView中的一个错误。根据{{3}},它是在iOS 5中引入的,并且在iOS 4.3及更低版本上执行不死锁。
有趣的是,在调用stringByEvaluatingJavaScriptFromString:
之前提出UIAlertView会奇怪地防止死锁:
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引擎在不同的线程上执行,因此它必须告诉主线程显示警报视图。但是,stringByEvaluatingJavaScriptFromString:
是一个返回值的阻塞调用。只有在单击“确定”关闭警报后才能返回该值。这就是死锁似乎发生的地方:从另一个线程我们告诉主线程告诉web视图运行JavaScript(发生在另一个线程上),这反过来告诉主线程显示一个警报视图,该视图只能单击“确定”后,将其返回值返回到JavaScript。只有当stringByEvaluatingJavaScriptFromString:
的调用返回时,我们传递给GCD的块才会完成。
答案 2 :(得分:0)
有几件事可能是其中的一个因素。正如其他人所提到的,JavaScript不在主线程上执行,而是在自己的后台线程上执行。您可能已经注意到代码的执行会一直等到JavaScript完成。我不知道代码中的具体实现,但听起来它在内部使用dispatch_sync
或dispatch_barrier_sync
。
您应该注意的下一个项目是UIAlertView -show
和JavaScript alert(..)
提供的警报之间存在显着差异。 UIAlertView -show
以异步方式运行。您可以这样说,因为在警报解除之前调用-show
后代码执行仍在继续。如果您在JavaScript中执行相同的实验,代码执行将停止,直到警报关闭。
最后,调度队列和线程之间存在差异。您可以在this thread中了解更多信息。我怀疑JavaScript执行正在执行[NSThread isMainThread]
并报告NO
因为它在主队列中,而不是主线程。由于此检查报告NO
,因此它会对主队列执行dispatch_sync
,主队列已被阻止,等待JavaScript队列。