- Вопрос или проблема
- Ответ или решение
- 1. Уменьшите количество одновременно загружаемых WKWebView
- 2. Используйте семафоры для ограничения параллельных операций
- 3. Убедитесь, что у вас нет множества WKWebView запущенных одновременно
- 4. Пробуйте размещать вызовы evaluateJavaScript в основном потоке
- 5. Следите за состоянием платформы
- Заключение
Вопрос или проблема
iOS18 WKWebview evaluateJavaScript вызывает сбой в нескольких потоках – ОШИБКА В КЛИЕНТЕ LIBPLATFORM: Попытка рекурсивной блокировки os_unfair_lock
После обновления до iOS18 мое приложение на iPhone продолжает аварийно завершаться с следующей ошибкой:
libsystem_platform.dylib`_os_unfair_lock_recursive_abort:
0x21852b0a4 <+0>: mov w8, w0
0x21852b0a8 <+4>: stp x20, x21, [sp, #-0x10]!
0x21852b0ac <+8>: adrp x20, 0
0x21852b0b0 <+12>: add x20, x20, #0x7f2 ; "BUG IN CLIENT OF LIBPLATFORM: Попытка рекурсивной блокировки os_unfair_lock"
0x21852b0b4 <+16>: adrp x21, 352637
0x21852b0b8 <+20>: add x21, x21, #0x1c0 ; gCRAnnotations
0x21852b0bc <+24>: str x20, [x21, #0x8]
0x21852b0c0 <+28>: str x8, [x21, #0x38]
0x21852b0c4 <+32>: ldp x20, x21, [sp], #0x10
-> 0x21852b0c8 <+36>: brk #0x1
Моё приложение открывает ~10 веб-страниц одновременно, используя WKWebview. Как только страница загружается, я использую evaluateJavaScript, чтобы получить html-содержимое страницы. Затем я использую kanna для разбора html-данных.
Код выглядит так:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript("document.documentElement.outerHTML.toString()", completionHandler: { (html: Any?, error: Error?) in
if (webView.url == nil) {
return
}
//использую kanna для разбора результата
})
}
После отладки, похоже, что всё работает нормально, если количество одновременно загружаемых веб-страниц составляет 2 или 3. Если я загружаю 10 различных веб-страниц одновременно, приложение аварийно завершится с ошибкой:
BUG IN CLIENT OF LIBPLATFORM: Попытка рекурсивной блокировки os_unfair_lock
Тот же код хорошо работал в iOS17 или ранее.
Я попробовал следующие подходы:
- поместить evaluateJavaScript в основной поток
- использовать блокировку/разблокировку перед evaluateJavaScript
Ни один из них не сработал в iOS18.
Я ожидал, что тот же код будет работать как и раньше.
Любая помощь, которую вы можете предоставить, будет очень оценена.
Ответ или решение
Проблема, с которой вы столкнулись, связана с использованием WKWebView
и многопоточностью в iOS 18. Ошибка:
BUG IN CLIENT OF LIBPLATFORM: Trying to recursively lock an os_unfair_lock
указывает на то, что вы пытаетесь выполнить рекурсивную блокировку, что не поддерживается os_unfair_lock
. Эта проблема может возникать, когда вы одновременно запускаете несколько операций, которые приводят к блокировке одного и того же ресурса. Ваша ситуация случайно приводит к конфликта между потоками при вызове evaluateJavaScript
.
Вот несколько рекомендаций, которые могут помочь решить данную проблему:
1. Уменьшите количество одновременно загружаемых WKWebView
Поскольку вы заметили, что при загрузке 2–3 страниц приложение работает нормально, попробуйте реализовать управление количеством загружаемых страниц. Вы можете использовать очередь, чтобы ограничить количество одновременно выполняемых операций:
let webViewQueue = DispatchQueue(label: "com.yourapp.webviewQueue")
func loadWebPage(url: URL) {
webViewQueue.async {
let webView = WKWebView()
webView.load(URLRequest(url: url))
webView.navigationDelegate = self
}
}
2. Используйте семафоры для ограничения параллельных операций
Вы можете использовать DispatchSemaphore
, чтобы ограничить количество одновременно выполняемых вызовов evaluateJavaScript
:
let semaphore = DispatchSemaphore(value: 3) // Максимум 3 потока одновременно
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
semaphore.wait()
webView.evaluateJavaScript("document.documentElement.outerHTML.toString()") { (html: Any?, error: Error?) in
defer { semaphore.signal() } // Освобождаем семафор
guard error == nil else {
print("Ошибка: \(error!)")
return
}
// используйте Kanna для парсинга результата
}
}
3. Убедитесь, что у вас нет множества WKWebView
запущенных одновременно
Проверьте, что все экземпляры WKWebView
управляются правильно и вы их не создаете без необходимости. Возможно, имеет смысл создавать и переиспользовать несколько экземпляров.
4. Пробуйте размещать вызовы evaluateJavaScript
в основном потоке
Если вы еще этого не сделали, убедитесь, что вызовы evaluateJavaScript
выполняются в главном потоке, используя:
DispatchQueue.main.async {
webView.evaluateJavaScript("...") { (html: Any?, error: Error?) in
// ваш код
}
}
5. Следите за состоянием платформы
Возможно, наличие ошибок в iOS 18 будет связано с самой платформой. Убедитесь, что у вас установлены все последние обновления. Если проблема сохраняется, возможно, стоит рассмотреть возможность обращения в техническую поддержку Apple или подать баг-репорт через Apple Bug Reporter.
Заключение
Так как ваш код работал корректно в iOS 17, вышеуказанные подходы должны помочь адаптировать его к изменениям в iOS 18. Управление потоками и синхронизацией — ключевые моменты для успешного решения проблемы. Попробуйте предложенные методы и наблюдайте за поведением вашего приложения. Если у вас возникнут дополнительные вопросы или проблемы, не стесняйтесь обращаться за помощью.