ПрограммированиеiOS разработчик

Как в Swift реализовать потокобезопасное взаимодействие с изменяемыми данными? Опишите подходы и проблемы, которые могут возникнуть при неправильной реализации.

Проходите собеседования с ИИ помощником Hintsage

Ответ

В Swift для обеспечения потокобезопасности часто используются GCD (Grand Central Dispatch) и очереди (DispatchQueue), NSLock, а также современные механизмы, такие как actor (начиная с Swift 5.5).

Основная идея – доступ к изменяемым данным происходит либо строго через последовательные очереди, либо с использованием синхронизаторов (замков).

Пример с GCD:

class ThreadSafeArray<Element> { private var array: [Element] = [] private let queue = DispatchQueue(label: "com.example.arrayQueue", attributes: .concurrent) func append(_ item: Element) { queue.async(flags: .barrier) { self.array.append(item) } } func get(index: Int) -> Element? { var result: Element? queue.sync { result = self.array.indices.contains(index) ? self.array[index] : nil } return result } }

Современный подход: Actors (Swift 5.5+):

actor SafeCounter { private var value = 0 func increment() { value += 1 } func get() -> Int { return value } }

Оба подхода позволяют избежать гонок данных и сохранять согласованность состояния.

Вопрос с подвохом

Вопрос:

Если вы используете DispatchQueue.sync внутри Main Queue, что произойдет и почему?

Ответ: Возникнет дедлок (Deadlock), потому что Main Queue уже выполняет задачу и ждет завершения синхронной операции на той же самой очереди. Следовательно, очередь не сможет обработать следующую задачу, пока текущая не завершится, что никогда не произойдет.

Пример:

DispatchQueue.main.sync { // Дедлок: эта строка никогда не выполнится }

Примеры реальных ошибок из-за незнания тонкостей темы


История

В проекте использовалась изменяемая коллекция, к которой обращались одновременно из нескольких потоков без синхронизации. Это приводило к падениям приложения и трудноуловимым багам из-за гонки данных: периодически терялись элементы, иногда происходил выход за границы массива.


История

Разработчик некорректно использовал DispatchQueue.sync на главной очереди в рантайме, что вызвало дедлок и полный стоп UI у пользователей после выхода обновления. Требовался срочный фикс и откат релиза.


История

При попытке сделать класс потокобезопасным с помощью NSLock, забыли реализовать lock/unlock для всех путей выхода из метода (например, в случае ошибки или return внутри guard), что приводило к потенциалу дедлоков и серьезным проблемам с производительностью приложения.