Pull to refresh
14
0
Вячеслав Редькин @SlaF

iOS developer

Send message

Я не знаю, как Apple считает память в инструментах Xcode. Возможно, внутри себя инструменты смотрят на то, что на объект нет сильных ссылок и считают память под него освобожденной. Но в саму операционную систему она еще не вернулась.

Или представьте такой случай: strong счетчик обнулился и мы освободили память. При этом еще есть unowned ссылки и нужно отслеживать некорректное обращение к ним. И в следующий момент операционная система может что угодно записать в память, где лежит счетчик ссылок. Тогда алгоритм подсчета ссылок сломается и если где-то в коде будет обращение к деинициализированной unowned переменной, рантайм не сообщит нам точно об этом. Просто операционная система увидит, что мы обращаемся к освобожденной памяти и выдаст EXC_BAD_ACCESS.

Когда я исследовал алгоритм работы счетчика, я тоже не мог представить, что объект живет так долго) И таких неочевидных моментов в рантайме языка, я думаю, еще много.

Да, HeapObject это и есть объект. И да, память под объект удаляется только после того, как unowned счетчик равен нулю. Вот ссылка на кейс в компиляторе, когда обвнуляется счетчик unowned ссылок: https://github.com/apple/swift/blob/17358e4d98f1cf0c5e8b2e8fd7bb740f663e6149/stdlib/public/runtime/HeapObject.cpp#L481. .

Для пользователя выглядит так, что объекта даже физически нет в памяти. Но чтобы обращаться к счетчику ссылок в рантайме, нужно держать память под него. Иначе счетчик пришлось бы выносить куда-то за пределы объекта. И в этом случае уже можно спокойно освобождать память под объект.

Лично я уже давно интересуюсь темой компиляторов, но никогда об этом не писал на хабр. А насчет применения в проекте - иногда приходят креши с очень странными стектрейсами. У нас был случай, когда в одной структуре мы использовали безобидный property wrapper и на проде приложение падало. Я собрал релизную сборку на своей машине и поймал этот креш. И уже через пару часов выяснилось, что компилятор сгенерировал вызов swift_release для поля структуры с типом String. Причем, над этим полем не было property wrapper и на сборке в оптимизациями все работает отлично. Тогда мы просто убрали этот property wrapper и перестали падать.

Именно понимание процесса подсчета ссылок помогло мне разобраться с этим крешем. Возможно, эта статья поможет еще кому-то справится с подобной проблемой. Да и просто интересно покапаться в реализации магических штуковин внутри Swift)

Спасибо за вопрос! Weak счетчик отмеряет время жизни side table. Все обращения к weak переменным происходят через side table и нужно понимать, когда можно удалить ее из памяти.

А unowned счетчик отмеряет время жизни памяти, выделенной под объект. Так как счетчик ссылок лежит внутри объекта и рантайм постоянно с ним работает, то память нельзя освобождать до момента, пока не останется никаких ссылок.
По сути, после обнуления weak счетчика можно спокойно уничтожать side table - к ней уже точно не будет обращений. А обнуление unowned счетчика гарантирует то, что больше не будет обращений к памяти объекта и ее можно очищать.
Можно после обнуление strong счетчика сразу освобождать память под объект. Но потом при обращении к unonwed переменной мы не получим исключение от рантайма, с информацией о том, к какой переменной мы обратились неправильно. Вместо этого мы будем видеть критическую ошибку обращения к освобожденной памяти от операционной системы. Как, собственно, сделано с unwoned переменными в Objective-C.

Надеюсь, мой комментарий помог вам понять необходимость наличия трех ссылок. Интересно, конечно, было бы спросить самих авторов этого алгоритма посчета.

Спасибо! В состоянии freed указатель на объект, который хранится в WeakReference, затирается нулями. Дальше при обращении к weak переменной рантайм проверяет, что указатель нулевой и возвращает nil. Хорошее замечание, добавлю ответ в статью!

Спасибо, наша команда старалась сделать этот материал простым для понимая! Насчет памяти - слишком затратно затирать ее нулями. Поэтому после того, как объект уже деаллоцирован, его “отпечаток” остается. Но затем этот кусок памяти может быть выделен снова. И только после этого в него записываются уже новые данные.

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity