Dead end with GCD and webView

I found a problem that seems to cause a dead end in WebKit. If I run this code from the main thread, I see the warning correctly. I can click the β€œOK” button in the message and it is rejected and everything works well:

[theWebView stringByEvaluatingJavaScriptFromString:@"alert('hi');"]; 

If I make a small modification, then a warning message appears, but the OK button cannot be pressed - you cannot cancel the warning, and if you go to the application, it will depend on the call to stringByEvaluatingJavaScriptFromString :

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_main_queue(), ^{ [theWebView stringByEvaluatingJavaScriptFromString:@"alert('hi');"]; }); }); 

The only difference in the two is that in the second, it starts JS in the main thread in the context of the send queue.

On the other hand, if I do the following, then the hang does not occur:

 - (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]; }); 

Can someone shed light on what is going wrong to cause a hang?

EDIT:

Related questions:

Perform user interface changes in the main thread using dispatch_async or execute SelectorOnMainThread?
What is the difference between performSelectorOnMainThread and dispatch_async in the main queue? Grand Central Dispatch (GCD) vs. performSelector - needs a better explanation

A very similar question:

UIWebView stringByEvaluatingJavaScriptFromString freezes on iOS5.0 / 5.1 when called using GCD

+6
source share
3 answers

This seems to be just a bug in the UIWebView. According to this question , it was introduced in iOS 5 and made a dead end not on iOS 4.3 or lower.

Interestingly, the UIAlertView view right before calling stringByEvaluatingJavaScriptFromString: strangely prevents a dead end:

 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');"]; }); }); 

However, my theory is this: when I pause execution after a dead end, I see that WebThread is stopped at __psynch_mutexwait . Because the JavaScript engine runs in different threads, it must point to the main thread to display the alert view. However, stringByEvaluatingJavaScriptFromString: is a blocking call that returns a value. The value can only be returned after the warning has been dismissed by clicking OK. This is where a dead end arises: from another thread, we tell the main thread to tell the web browser that it is running JavaScript (what happens in the other thread), which in turn tells the main thread to display a warning, which can return the return value back to JavaScript after clicking OK. And only when the call to stringByEvaluatingJavaScriptFromString: returns, is it the block that we passed to the GCD.

There must be a mistake. It is strange that a dead end does not occur when I first show the UIAlertView. Perhaps, in this case, iOS puts the second type of warning on some line, which prevents a dead end. Weird!

+4
source

I think what was pointed out in webview Image class reference

Now, for your case of deadlock, you can simulate the same thing by replacing it with [self performSelectorOnMainThread:@selector(showHi:) withObject:theWebView waitUntilDone:NO];

with

performSelector:withObject:afterDelay:inModes: With the corresponding default mode, it was NSDefaultRunLoopMode and was atomic in nature, whereas in your non-atomic case dispatch_get_main_queue() you need to change the current mode of sending the stream.

I hope he stayed informative. More suggestions are also welcome.

+5
source

There are several things that are probably a factor in this. As others have noted, JavaScript is not executed in the main thread, but rather in its own background thread. You have probably noticed that the execution of your code is awaiting completion of JavaScript. I don’t know the specific implementation in the code, but it looks like it uses dispatch_sync or dispatch_barrier_sync internally.

The next element that you should notice is that there is a significant difference between the warnings presented in the UIAlertView -show and the JavaScript alert(..) . UIAlertView -show works asynchronously. This can be said because code execution continues after the -show call until the warning is rejected. If you run the same experiment in JavaScript, the execution of the code stops until the warning is closed.

Finally, there is a difference between send queues and threads. You can read more in this thread about this. I suspect that JavaScript is executing [NSThread isMainThread] and it reports NO because it is in the main queue, but not in the main thread. Since this check reports NO , it runs dispatch_sync on the main queue, which is already blocked, waiting for the JavaScript queue.

0
source

Source: https://habr.com/ru/post/956519/


All Articles