Какую роль играет явное обращение к self внутри escaping-замыкания Swift?
Явное обращение к self показывает компилятору и читателю, что замыкание захватывает экземпляр. Само написание self не создаёт цикл удержания, но делает захват заметным и позволяет осознанно выбрать между сильной, слабой или нефатальной ссылкой.
Замыкания в Swift могут пережить функцию, в которой были созданы, и сохранять захваченные значения. Поэтому язык сделал захват self в escaping-замыканиях явным: это снижает риск незаметного продления времени жизни объекта и облегчает обнаружение циклов удержания при использовании ARC.
В неescaping-замыканиях компилятор обычно может безопаснее анализировать время жизни замыкания, поэтому явное self во многих таких случаях не требуется.
Если экземпляр хранит замыкание, а замыкание сильно захватывает этот же экземпляр, возникает цикл: экземпляр удерживает замыкание, замыкание удерживает экземпляр. В результате ARC не может освободить объект после исчезновения внешних ссылок.
Неявный захват был бы особенно опасен в асинхронном коде, где замыкание может выполняться значительно позже завершения метода. Явное self не устраняет проблему автоматически, но заставляет разработчика увидеть факт захвата и выбрать подходящую семантику.
Сильный захват является стандартным поведением для ссылки на self внутри escaping-замыкания. Если замыкание хранится самим объектом, такой захват обычно образует цикл удержания.
В примере список захвата [weak self] хранит слабую ссылку. После освобождения объекта она становится nil, поэтому вызов через self? ничего не выполняет. Это предотвращает цикл, но требует учитывать, что объект может исчезнуть до выполнения замыкания.
unowned self также не удерживает объект, но предполагает, что объект гарантированно будет жить дольше замыкания. Нарушение этого предположения приводит к аварийному завершению, поэтому unowned подходит только при доказуемо связанном времени жизни.
Важно: явное self не меняет сильный захват на слабый. Оно лишь делает обращение к экземпляру явным; способ захвата задаётся списком захвата и выбранной семантикой ссылки.
Экран запускает сетевой запрос и сохраняет completion-замыкание в объекте запроса. Если completion сильно захватывает экран, а экран удерживает объект запроса, после ухода со страницы может остаться цикл удержания.
Вариант с сильным захватом проще: экран гарантированно существует до завершения операции, но возможна утечка при взаимном владении. Вариант с weak self безопаснее для памяти, однако экран может исчезнуть раньше, и результат будет проигнорирован. Вариант с unowned self не создаёт цикл и не требует optional-проверки, но аварийно завершит приложение при ошибочном предположении о времени жизни.
Для обычного сетевого запроса выбранным решением обычно будет weak self, если обновление экрана не должно удерживать экран. Это устраняет цикл и корректно допускает отмену или исчезновение интерфейса.
Нет. Цикл возникает из-за сильного захвата объекта замыканием в сочетании с обратным владением замыканием со стороны объекта. Явное self только делает этот захват синтаксически видимым. Если замыкание не хранится объектом или объект не владеет замыканием, одного обращения к self недостаточно для образования цикла.
Слабая ссылка не продлевает время жизни объекта и автоматически обнуляется после его освобождения. Поэтому значение, полученное через weak self, может отсутствовать в момент выполнения замыкания. Код должен безопасно обработать nil, например через optional chaining или ранний выход.
Нет. Если замыкание не хранится объектом, сильный захват может быть правильным: он гарантирует существование объекта до завершения операции. weak self следует выбирать не по шаблону, а исходя из владения и требуемого времени жизни; без него объект может исчезнуть раньше, чем замыкание выполнит необходимую работу.