Опишите, кто отвечает за время жизни экземпляра Swift, переданного через Unmanaged.
Unmanaged не удерживает объект автоматически: он лишь оборачивает ссылку без обычного управления ARC. Ответственность зависит от способа передачи: passRetained добавляет владение, которое затем нужно сбалансировать через takeRetainedValue, а passUnretained владение не добавляет.
Нарушение этого баланса приводит либо к преждевременному уничтожению объекта и обращению к невалидной ссылке, либо к утечке памяти.
ARC автоматизирует retain/release для обычных ссылок Swift и устраняет необходимость вручную управлять счётчиком владения. Однако при взаимодействии с C и Objective-C встречаются непрозрачные указатели, callback-механизмы и соглашения о владении, которые нельзя безопасно выразить обычным свойством или параметром Swift.
Unmanaged предоставляет контролируемый escape hatch для таких случаев. Он позволяет явно выбрать, нужно ли передавать владение объектом, но переносит ответственность за корректный баланс операций на разработчика.
Если передать объект через Unmanaged.passUnretained, дополнительной сильной ссылки не появится. После уничтожения исходного объекта сохранённое значение может указывать на освобождённую память, поэтому вызов takeUnretainedValue безопасен только при гарантированно живом объекте.
При использовании passRetained создаётся дополнительное владение. Если его не забрать через takeRetainedValue или явно не освободить другим предусмотренным способом, объект останется жить дольше необходимого, что приведёт к утечке.
Unmanaged<T> — это оболочка над ссылкой, которая сама по себе не участвует в обычном подсчёте сильных ссылок. Основные пары операций выглядят так:
passUnretained и takeUnretainedValue не изменяют владение;passRetained передаёт значение с дополнительным владением;takeRetainedValue извлекает объект и балансирует это дополнительное владение.Минимальный пример корректной пары:
После passRetained локальная сильная ссылка token в функции может исчезнуть, но дополнительное владение, переданное в Unmanaged, сохраняет объект. takeRetainedValue забирает это владение, после чего обычная сильная переменная token продолжает управлять временем жизни экземпляра.
Нельзя бездумно заменять одну операцию другой. Вызов takeUnretainedValue для значения, созданного через passRetained, нарушит баланс и может вызвать утечку; использование takeUnretainedValue после passUnretained при уже уничтоженном объекте создаёт обращение к невалидной памяти.
Unmanaged не делает код безопаснее и не проверяет, жив ли объект. Его следует применять только там, где точно известны соглашения о владении внешнего API или протокола callback-взаимодействия. Для обычного Swift-кода предпочтительны стандартные сильные, слабые и невладеющие ссылки.
Системный C API принимает UnsafeMutableRawPointer и возвращает его в callback. В callback нужно восстановить экземпляр Swift, но API не хранит информацию о том, должен ли он удерживать объект.
Вариант с passUnretained дешевле и не создаёт дополнительного владения, но требует гарантии, что объект будет жить до завершения callback. Он опасен, если внешний API может вызвать callback асинхронно после уничтожения исходного объекта.
Вариант с passRetained безопаснее для асинхронной передачи: дополнительное владение сохраняет объект до момента обработки callback. Его недостаток — необходимость строго выполнить парную операцию, иначе возникнет утечка.
Практический выбор — passRetained для callback, который может быть вызван позже, и takeRetainedValue ровно один раз внутри callback. Если API предоставляет собственное явное соглашение о времени жизни, соответствующее ему passUnretained может быть уместнее и не потребует лишнего retain/release.
Unmanaged сильной ссылкой?Нет. Само значение Unmanaged<T> не продлевает жизнь экземпляра. Сильным является только владение, которое было явно создано через passRetained либо существует отдельно в обычной Swift-ссылке.
takeRetainedValue дважды?Первый вызов забирает переданное владение. Второй вызов не получает новое владение и нарушает контракт использования значения. Результат — неопределённое поведение с риском преждевременного освобождения, повреждения управления памятью или аварийного завершения.
Unmanaged не нужен даже при работе с указателями?Если объект можно удерживать обычной сильной ссылкой до завершения операции, безопаснее сделать именно это и передавать наружу только указатель на время гарантированно живого объекта. Unmanaged оправдан, когда требуется явно передать или забрать ownership через внешний API; для простого временного доступа он добавляет риск несбалансированного владения без практической пользы.