Поддерживает ли ARC в Swift потокобезопасность счетчика ссылок
Что проверяют
Различаете ли вы потокобезопасность счётчика ссылок и потокобезопасность самого объекта. Вопрос с ловушкой: правильный ответ утвердительный, но с большой оговоркой, и именно оговорка показывает реальный опыт с многопоточностью.
Как отвечать
ARC это не отдельный сборщик в рантайме, а вызовы retain и release, расставленные компилятором. Сам счётчик обновляется атомарными операциями, поэтому одновременные удержание и освобождение из разных потоков его не разрушат: значение не потеряется и объект не умрёт раньше времени только из-за гонки за счётчик.
Дальше главное: атомарность счётчика не делает потокобезопасными ни объект, ни переменную, которая на него ссылается. Присваивание в var это несколько шагов: прочитать старую ссылку, удержать новую, освободить старую. Два потока, пишущие в одну и ту же сильную переменную, дают лишний release и обращение к освобождённой памяти, то есть падение, а не аккуратную перезапись. Слабые ссылки живут в отдельной таблице и корректно обнуляются, но и они не отменяют гонку за саму переменную.
Отсюда и ответ про защиту: изоляция через актор или собственную очередь, блокировка (NSLock, os_unfair_lock) вокруг доступа к общему состоянию, либо значимые типы и неизменяемость. Полезно упомянуть, что копирование при записи у массивов и словарей опирается на проверку уникальности ссылки, и корректна она как раз благодаря атомарному счётчику.
Где спотыкаются
Отвечают, что счётчик не потокобезопасен и вокруг retain нужен лок. Всё наоборот: счётчик атомарен, а лок нужен вокруг вашего состояния и вокруг записи в саму ссылку.
Описывают последствие гонки как утечку памяти. Утечка это про циклы сильных ссылок, а гонка за ссылкой даёт преждевременное освобождение и падение, которое не воспроизводится и выглядит случайным.
Ставят знак равенства между ARC и сборщиком мусора. ARC не обходит граф и не разрывает циклы, weak и unowned остаются заботой разработчика, а атомарные операции над счётчиком не бесплатны в горячем коде.