iOS18 WKWebview evaluateJavaScript вызывает сбой в нескольких потоках – ОШИБКА В КЛИЕНТЕ LIBPLATFORM: Попытка рекурсивной блокировки os_unfair_lock

Вопрос или проблема

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 или ранее.

Я попробовал следующие подходы:

  1. поместить evaluateJavaScript в основной поток
  2. использовать блокировку/разблокировку перед 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. Управление потоками и синхронизацией — ключевые моменты для успешного решения проблемы. Попробуйте предложенные методы и наблюдайте за поведением вашего приложения. Если у вас возникнут дополнительные вопросы или проблемы, не стесняйтесь обращаться за помощью.

Оцените материал
Добавить комментарий

Капча загружается...