Что такое NSLock в iOS
Что проверяют
Понимаете ли вы, как устроена синхронизация на платформе Apple, и умеете ли выбирать инструмент. NSLock это самый простой взаимный замок, и вопрос обычно раскручивается до сравнения с последовательной очередью и акторами, а также до правил, которые этот замок легко нарушить.
Как отвечать
NSLock это обёртка над мьютексом операционной системы: lock блокирует, unlock освобождает, tryLock пробует захватить без ожидания. Задача одна, дать эксклюзивный доступ к изменяемому состоянию из нескольких потоков.
final class Counter {
private var value = 0
private let lock = NSLock()
func increment() {
lock.lock()
defer { lock.unlock() }
value += 1
}
}Что важно проговорить:
- Замок не рекурсивный: повторный захват в том же потоке это дедлок. Для рекурсии есть NSRecursiveLock, но чаще это признак неудачно разрезанного кода.
- Освобождать должен тот же поток, который захватил.
- Последовательная очередь решает ту же задачу, но стоит дороже на диспетчеризации. Утверждение, что очередь быстрее замка, неверно: для короткой критической секции берут замок, для потока задач очередь.
- В асинхронном коде вместо замков лучше работают акторы: состояние изолируется на уровне типа.
Где спотыкаются
Держат замок через точку приостановки. После продолжения работа может идти уже в другом потоке, а освобождать обязан захвативший, и получается неопределённое поведение вплоть до зависания. Замком закрывают короткий синхронный участок, всё остальное выносят наружу.
Выходят из метода мимо unlock: ранний возврат по условию или брошенная ошибка оставляют замок захваченным навсегда. Именно поэтому освобождение пишут отложенным вызовом сразу после захвата.
Захватывают два замка в разном порядке в разных местах кода. Два потока встают друг напротив друга и ждут вечно, причём воспроизводится это редко и не на машине разработчика. Спасает единый порядок захвата или отказ от второго замка.