Два escaping-замыкания захватывают одну локальную переменную var: какое состояние увидит второе после изменения, выполненного первым?
Второе замыкание увидит изменённое значение. При захвате общей локальной var Swift помещает её состояние в общее хранилище, доступное обоим замыканиям, поэтому они работают с одной переменной, а не с независимыми копиями.
Замыкания должны уметь обращаться к локальным переменным даже после завершения функции, в которой эти переменные были объявлены. Это необходимо для отложенных обработчиков, completion-блоков и callback-механизмов.
Чтобы сохранить такое состояние, Swift отделяет переменную от стека текущего вызова и помещает захваченное состояние в специальное окружение замыкания. Если несколько замыканий захватывают одну переменную, они получают доступ к общему окружению.
Интуитивно можно предположить, что каждое замыкание получает собственную копию значения. Для изменяемой переменной это неверно: такое предположение приводит к ошибкам при реализации счётчиков, повторных callback-вызовов и замыканий, которые должны обмениваться состоянием.
Общее состояние также создаёт риск гонки данных, если замыкания вызываются одновременно из разных потоков. Сам факт использования escaping не делает доступ к захваченной переменной потокобезопасным.
Для захваченной изменяемой переменной Swift обычно формирует общее хранилище, иногда называемое box. Оба escaping-замыкания хранят ссылку на это хранилище: одно может изменить значение, а другое прочитает уже обновлённое состояние.
После завершения makeActions переменная не исчезает, потому что её хранилище удерживается возвращёнными замыканиями. Если вместо общей переменной использовать список захвата, например [counter], замыкание захватит значение на момент создания, и последующие изменения исходной переменной не будут видны.
Захват общей переменной не означает автоматическую синхронизацию. Для конкурентного доступа нужны подходящие средства изоляции и синхронизации; в современном Swift дополнительные ограничения могут применяться к @Sendable-замыканиям.
Сервис создаёт два callback-а: первый увеличивает число повторных попыток, второй формирует диагностическое сообщение с текущим числом. Если оба callback-а должны видеть единый счётчик, общая захваченная переменная подходит: состояние сохраняется между вызовами и доступно обоим замыканиям.
Вариант с отдельными копиями проще локально, но разрушает связь между callback-ами. Вариант с глобальной переменной делает состояние доступным слишком широко и усложняет тестирование. Поэтому выбирают локальную переменную, захваченную обоими замыканиями, а при возможных одновременных вызовах дополнительно изолируют доступ актором или другим механизмом синхронизации.
Int при захвате несколькими замыканиями?Нет, если замыкания захватывают одну изменяемую локальную переменную. Копироваться может само значение при чтении, но операции выполняются через общее хранилище переменной. Поэтому изменение, выполненное одним замыканием, доступно другому.
[counter]?Список захвата фиксирует значение в момент создания замыкания. Такое замыкание получает собственное захваченное значение, поэтому последующее изменение исходной переменной не меняет уже захваченную копию. Список захвата применяют, когда нужен снимок состояния, а не совместно изменяемая переменная.
Нет, общий захват сам по себе не обеспечивает потокобезопасность. Одновременная запись и чтение или две одновременные записи могут привести к гонке данных и неопределённым результатам. Доступ следует изолировать средствами Swift Concurrency, например актором, либо защищать проверенным механизмом синхронизации.