Ответ
Да, можно обновлять массив из фонового потока, но массивы Swift не являются потокобезопасными. Прямое изменение из нескольких потоков вызывает race condition и может крашить приложение.
Решение 1: GCD с барьером (iOS 4+)
class ThreadSafeArray<T> {
private var array: [T] = []
private let queue = DispatchQueue(label: "com.app.threadSafeArray",
attributes: .concurrent)
func append(_ element: T) {
queue.async(flags: .barrier) {
self.array.append(element)
// Обновление UI должно быть на main
DispatchQueue.main.async {
// UI обновления здесь
}
}
}
var values: [T] {
queue.sync { array } // Безопасное чтение
}
}
Решение 2: Actor (Swift 5.5+, iOS 13+)
actor SafeArray<T> {
private var array: [T] = []
func append(_ element: T) {
array.append(element)
}
func getAll() -> [T] {
return array
}
}
// Использование:
let safeArray = SafeArray<String>()
Task {
await safeArray.append("Item from background")
let items = await safeArray.getAll()
}
Решение 3: @MainActor для UI-обновлений
@MainActor
class ViewModel: ObservableObject {
@Published var items: [String] = []
func fetchData() async {
let newItems = await fetchFromBackground()
// Автоматически на главном потоке благодаря @MainActor
items.append(contentsOf: newItems)
}
}
Критические правила:
- Все модификации массива должны быть синхронизированы
- Чтение тоже требует синхронизации (race condition возможен при чтении во время записи)
- Для UI-обновлений всегда переключайтесь на главный поток
- Используйте
NSLockилиos_unfair_lockдля микрооптимизаций - Рассмотрите готовые решения вроде
Atomicиз Swift Collections