Когда ARC начинает учитывать время жизни экземпляра, возвращаемого выражением @autoclosure, переданным escaping-параметру?
Для результата выражения @autoclosure ARC начинает управлять временем жизни объекта при фактическом вычислении этого выражения, а не в момент передачи аргумента. Пока escaping-замыкание не вызвано, экземпляр, создаваемый внутри выражения, обычно ещё не существует.
Однако само замыкание может заранее удерживать объекты, которые нужны для вычисления выражения. Поэтому нужно различать время жизни захваченных входных объектов и объекта-результата.
@autoclosure появился как механизм отложенного вычисления выражения при сохранении обычного синтаксиса вызова. Это удобно для проверок, логирования и API, где вычисление аргумента может быть дорогим или иметь побочные эффекты.
Если параметр дополнительно объявлен как escaping, созданное замыкание может пережить текущий вызов функции. Вместе с ним могут сохраняться его захваченные зависимости, что напрямую влияет на владение объектами.
Передача выражения в @autoclosure не означает немедленного создания объекта, возвращаемого этим выражением. Ошибка возникает, когда разработчик считает, что такой объект уже удерживается ARC до вызова отложенного замыкания.
Обратная проблема тоже возможна: выражение использует существующий объект, и escaping-замыкание сильно захватывает его ещё до вычисления результата. Тогда объект может жить дольше ожидаемого, даже если результат выражения пока не создан.
@autoclosure преобразует выражение аргумента в замыкание без аргументов. Если это замыкание escaping, функция может сохранить его и вызвать позже. До вызова тело выражения не выполняется, поэтому вызов конструктора или фабричной функции внутри него не происходит.
Сначала создаётся escaping-замыкание, но makeProbe() вызывается только при deferred(). После этого возвращённый экземпляр удерживается локальной сильной ссылкой probe; после её освобождения объект становится доступным для уничтожения, если других сильных ссылок нет.
Есть важное ограничение: захват переменных для вычисления выражения происходит при формировании замыкания. Например, если отложенное выражение обращается к экземпляру через сильную локальную переменную, замыкание может удерживать этот экземпляр ещё до вызова. @autoclosure откладывает вычисление, но не превращает захваты автоматически в слабые.
Сервис аналитики принимает отложенное сообщение и сохраняет его для фоновой отправки. В сообщение входит обращение к контроллеру, который уже должен быть освобождён после закрытия экрана.
Вариант с обычным сильным захватом прост и безопасен для доступа к контроллеру, но может продлить жизнь экрана на весь срок хранения задачи. Вариант с weak захватом не создаёт такого удержания, однако сообщение может не сформироваться, если контроллер исчезнет до фактического вычисления.
Практическое решение — не передавать в отложенное выражение весь контроллер. Сначала извлечь независимые данные, необходимые для сообщения, а затем передать отложенное вычисление, не захватывающее экран. Это уменьшает граф владения и делает время жизни объектов предсказуемым.
Создаёт ли @autoclosure объект в момент передачи аргумента?
Нет. Оно откладывает выполнение выражения. Если выражение вызывает инициализатор или фабричную функцию, экземпляр появится только при вызове созданного замыкания.
Может ли объект существовать до вычисления результата @autoclosure?
Да, если он является захваченной зависимостью выражения. Например, обращение к свойству экземпляра может привести к сильному захвату самого экземпляра escaping-замыканием. Время жизни входного объекта и время жизни результата — разные вещи.
Освобождается ли результат сразу после вызова отложенного замыкания?
Не обязательно. После вычисления результат получает обычное управление ARC: его могут удерживать локальная переменная, свойство, коллекция, другое замыкание или возвращающий код. Объект становится доступным для уничтожения только после освобождения последней сильной ссылки.