Ответ
Нет, Run Loop не является потокобезопасным. Каждый поток (включая главный) имеет максимум один связанный с ним объект RunLoop, и работать с ним можно только в контексте этого потока.
Почему это важно: Попытка вызова методов RunLoop (таких как run(), add(_:forMode:)) из потока, отличного от того, которому он принадлежит, приводит к неопределенному поведению и часто к крашам.
Правило: Все манипуляции с RunLoop.current должны выполняться в том же потоке, где этот RunLoop был получен.
Пример опасного кода:
DispatchQueue.global().async {
// ОШИБКА: Попытка запустить RunLoop фонового потока не из него самого.
// Это может заблокировать поток или привести к сбою.
RunLoop.current.run(mode: .default, before: .distantFuture)
}
Правильный подход для фоновых потоков:
let backgroundThread = Thread {
// 1. Получаем RunLoop ТЕКУЩЕГО (фонового) потока.
let runLoop = RunLoop.current
// 2. Добавляем источник событий (например, порт), чтобы RunLoop не завершился сразу.
runLoop.add(Port(), forMode: .default)
// 3. Запускаем RunLoop В ЭТОМ ЖЕ потоке.
// RunLoop будет обрабатывать события, пока не будет остановлен.
runLoop.run()
}
backgroundThread.start()
Для межпоточного взаимодействия вместо прямого управления Run Loop другого потока используйте механизмы, предназначенные для этого:
- GCD (
DispatchQueue):async,sync. OperationQueue.performSelector(on:with:waitUntilDone:)— это безопасный способ поставить задачу в очередь на выполнение в Run Loop целевого потока.
// Безопасное выполнение кода в RunLoop главного потока из фонового:
DispatchQueue.global().async {
// ... фоновая работа ...
DispatchQueue.main.async {
// Код выполнится в RunLoop главного потока.
self.updateUI()
}
}