Освобождает ли ARC память, выделенную через UnsafeMutableRawPointer.allocate?
Нет. ARC управляет временем жизни экземпляров классов и сильных ссылок на них, но не отслеживает память, выделенную через UnsafeMutableRawPointer.allocate. Такую память нужно явно освободить вызовом deallocate, обычно в deinit владельца.
ARC появился как способ автоматизировать подсчёт ссылок на объекты без ручных вызовов retain и release. Он решает задачу управления временем жизни ссылочных объектов, но не заменяет общий механизм управления произвольной памятью, потому что не может определить, кому принадлежит каждый указатель и когда его содержимое больше не используется.
Если класс выделил буфер через указатель, а затем его экземпляр уничтожен ARC, сам буфер не будет автоматически освобождён. При отсутствии явного deallocate возникает утечка памяти, даже если deinit класса был вызван.
Обратная ошибка также опасна: повторный вызов deallocate, использование указателя после освобождения или передача владения нескольким независимым владельцам приводит к неопределённому поведению.
Нужно связать время жизни буфера с объектом-владельцем и освободить память ровно один раз:
В примере ARC уничтожает BufferOwner, когда исчезает последняя сильная ссылка. Во время deinit вызывается ручное освобождение буфера. Само наличие свойства типа UnsafeMutableRawPointer не делает его управляемым ARC: это значение-указатель, а не ссылочное владение объектом.
Для корректности количество байтов, выравнивание и способ освобождения должны соответствовать выделению. Если указатель копируется, копируются только адреса; ARC не создаёт дополнительное владение памятью и не предотвращает двойное освобождение.
Если объект-владелец попал в retain cycle, его deinit не выполнится, поэтому связанный с ним буфер также останется выделенным. Для сложного владения лучше инкапсулировать указатель в отдельном типе с явно определённым единственным владельцем или использовать безопасные контейнеры Swift, когда это возможно.
Низкоуровневый компонент хранит временный буфер для обмена с C-библиотекой. Рассматривались два варианта: освобождать память вручную в каждом месте завершения операции или хранить её в объекте-владельце и освобождать в deinit.
Первый вариант сложнее проверить: при раннем выходе, ошибке или исключении легко пропустить освобождение. Второй вариант уменьшает число путей очистки и связывает ресурс с временем жизни владельца, но требует исключить циклы владения и не передавать указатель наружу без чётких правил использования.
Выбран объект-владелец с одним ответственным за освобождение указателем. В результате обычное уничтожение владельца корректно освобождает буфер, а код операций не содержит дублирующей логики очистки.
Что произойдёт с буфером, если объект-владелец удерживается циклом сильных ссылок?
deinit не будет вызван, потому что ARC не сможет уменьшить число сильных ссылок до нуля. Следовательно, код deinit, включая deallocate, не выполнится, и буфер останется выделенным. Для устранения проблемы нужно разорвать цикл, например заменить одну из ссылок на weak или unowned, если это соответствует гарантии времени жизни.
Становится ли копия UnsafeMutableRawPointer вторым владельцем выделенной памяти?
Нет. Копирование указателя создаёт второй адресный дескриптор, но не увеличивает счётчик владения и не регистрируется в ARC. Поэтому один из владельцев должен быть явно определён ответственным за deallocate, а остальные копии нельзя использовать после освобождения памяти.
Можно ли хранить такой указатель в weak-свойстве, чтобы ARC управлял его временем жизни?
Нет. weak применяется к ссылкам на экземпляры классов, а UnsafeMutableRawPointer является значением-типом и не представляет ARC-объект. Срок жизни выделенной памяти нужно контролировать отдельно: через deinit, специальный владеющий контейнер или другой явно определённый механизм управления ресурсом.