Тупик с GCD и webView

Я нашел проблему, которая, по-видимому, вызывает тупик в WebKit. Если я запустил этот код из основного потока, я правильно вижу предупреждение. Я могу нажать кнопку «ОК» на оповещение, и он отклоняется, и все работает хорошо:

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

Если я сделаю небольшую модификацию, тогда появится предупреждающее сообщение, но кнопка «ОК» не может быть задействована – вы не можете отменить предупреждение, и если вы stringByEvaluatingJavaScriptFromString в приложение, оно будет stringByEvaluatingJavaScriptFromString вызове 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 или выполнитьSelectorOnMainThread?
В чем разница между performSelectorOnMainThread и dispatch_async в главной очереди?
Grand Central Dispatch (GCD) против performSelector – требуется лучшее объяснение

Очень похожий вопрос:

UIWebView stringByEvaluatingJavaScriptFromString зависает на iOS5.0 / 5.1 при вызове с использованием GCD

Кажется, это просто ошибка в UIWebView. Согласно этому вопросу , он был введен в iOS 5 и не зашел в тупик на iOS 4.3 и ниже.

Интересно, что представление 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 выполняется в разных потоках, он должен указать основному потоку, чтобы отобразить представление предупреждения. Однако stringByEvaluatingJavaScriptFromString: – это блокирующий вызов, который возвращает значение. Значение может быть возвращено только после того, как предупреждение будет уволено, нажав OK. Здесь возникает тупик: из другого потока мы говорим основному потоку, чтобы сообщить веб-браузеру, что он запускает JavaScript (что происходит в другом потоке), что, в свою очередь, указывает основному потоку отображать предупреждение, которое может верните возвращаемое значение обратно на JavaScript после нажатия OK. И только когда вызов stringByEvaluatingJavaScriptFromString: возвращается – это блок, который мы передали в GCD.

Это, должно быть, ошибка. Странно, что тупик не возникает, когда я сначала показываю UIAlertView. Возможно, в этом случае iOS ставит второй вид предупреждения на какую-то очередь, что предотвращает тупик. Странный!

Я думаю, что это было указано в webview Образ ссылка на класс

Теперь, для вашего случая мертвой блокировки вы можете имитировать то же самое, заменив это [self performSelectorOnMainThread:@selector(showHi:) withObject:theWebView waitUntilDone:NO];

с

performSelector:withObject:afterDelay:inModes: С соответствующим режимом по умолчанию это был NSDefaultRunLoopMode и был атомарным по своей природе, тогда как в вашем неатомическом случае dispatch_get_main_queue() вы должны изменить текущий режим отправки потоковой передачи.

Надеюсь, он остался информативным. Кроме того, больше предложений приветствуется.

В этом есть несколько факторов. Как отмечали другие, JavaScript не выполняется в основном потоке, а скорее в собственном фоновом потоке. Вероятно, вы заметили, что выполнение вашего кода ожидает завершения JavaScript. Я не знаю конкретной реализации в коде, но похоже, что она внутренне использует dispatch_sync или dispatch_barrier_sync .

Следующий пункт, который вы должны заметить, заключается в том, что существует значительная разница между предупреждениями, представленными в UIAlertView -show и JavaScript alert(..) . UIAlertView -show работает асинхронно. Вы можете это сказать, потому что выполнение кода продолжается после вызова -show перед -show предупреждения. Если вы выполняете этот же эксперимент в JavaScript, выполнение кода останавливается до тех пор, пока предупреждение не будет закрыто.

Наконец, существует разница между очередями отправки и потоками. Вы можете прочитать больше об этом в этой теме . Я подозреваю, что выполнение JavaScript выполняет [NSThread isMainThread] и оно сообщает NO потому что оно находится в главной очереди, но не в основном потоке. Поскольку эта проверка сообщает об NO , она dispatch_sync в основную очередь, которая уже заблокирована, ожидая очереди JavaScript.

  • WKWebView заставляет мой контроллер просмотра протекать
  • Как прокрутить палец прокрутки прокручиваемого div с помощью "webkit-overflow-scrolling: touch"
  • Каковы последствия использования неподдерживаемого свойства WebKit?
  • Обнаружение воспроизведения видео в компоненте WKWebView
  • Максимальный размер базы данных WebSQL / SQLite внутри UIWebView (телефонная задержка)
  • -webkit-transform Запрещает страницу с загрузки
  • Отключить усиление в WKWebView
  • Как получить слово в указанном месте на WKWebView?
  • HTML-разбивка на страницы
  • Предотвращение наложения серым цветом на сенсорный экран в мобильном браузере Safari / Webview
  • Ошибка прокрутки Webkit
  • Давайте будем гением компьютера.