Comments 11
Спасибо за статью - ?
Отличная статья, довольно подробно расписано. Я Android разработчик, но ознакомился для общего развития, как устроено у вас. Можно обновлять резюме :D
По умолчанию, пул не освобождает эти объекты в конце RunLoop'а, а уменьшает счётчик ссылкок на 1.
Информативная статья, как и телеграм-канал. Спасибо, что предоставляете возможность погрузиться в iOS-разработку
В этом-то и беда данной статьи - проскакать по верхам без понимания сути - получатся мемы типа байткода или даже целого абзаца:
Unowned ссылки имеют различную разновидность weak, рассчитанные на инварианты жесткой валидности. Необнуляемые ссылки не обнуляются. При попытке прочитать несуществующий объект по неизвестной ссылке программа выйдет из строя с ошибкой. Четкой причины их юзать до сих пор не знают и много спорят, но все выводы уходят в легкость дебагинга.
Автору посоветую в будущем отдавать свои статьи на вычитку, но лишь после того как ему самому станет понятна каждая выхваченная из других статей фраза.
Успехов.
Пропущены слова в абзаце про СТЕК: Если размер вашего value type может быть определен во время компиляции или если ваш не содержит рекурсию на себя или не находится в ссылочном типе, тогда потребуется выделение стека.
Ваш не содержит рекурсию?
А так спасибо за статью.
Уже идет работа по move-only типам (в виде атрибутов для локальных переменных, аргументов функций и возвратов, полей классов), правда непонятно как это будет работать value типами у которых внутреннее хранилище реализовано с использованием класса и будет ли deinit у структур.
С одной стороны, Вы пишите об управлении памятью в iOS и swift. С другой стороны, у Вас достаточно обязывающий заголовок: «Управление памятью в современных языках программирования». Поэтому можно окинуть критическим взглядом не только iOS и swift. И этот взгляд, упавший на swift показывает, что этот язык хоть и современный, но в некоторых вопросах (судя по Вашему описанию) он отнюдь не современен.
«Существует ограничение в том, что данные, которые предполагается хранить в стеке, обязаны быть конечными и статичными — их размер должен быть известен ещё на этапе компиляции»
В других языках это не всегда так. Тот же Си выходит за описанные Вами рамки двумя способами. Первый — с помощью функций с переменным количеством аргументов, которые, как известно, кладутся на стек перед вызовом функции. Хотя размер стекового кадра известен во время компиляции, но он не статичен, он меняется от вызова к вызову.
Второй способ — с помощью функции alloca, которая выделяет память не в куче, а в стеке. В этом случае размер стекового кадра не только не статичен, но ещё и неизвестен во время компиляции. Правда, пользоваться функцией не всегда удобно. Если, допустим, надо создать в стеке какой-то объект, то сперва надо вычислить его размер и только потом вызвать alloca. Такое преодоление пропасти в два прыжка мешает написать конструктор объекта, который бы сделал это всё сразу.
Проблему решает модель памяти не с одним, а двумя стеками. В ней цепочка подпрограмм использует стеки по очереди: чётные подпрограммы используют чётный стек, а нечётные — нечётный стек. Тогда конструктор, работающий в одном стеке, может создать объект вычисляемой длины в другом стеке. Такие манипуляции возможны потому, что локальные данные конструктора находятся в своём стеке и они не будут перекрываться создаваемым в другом стеке объектом. Подробнее можно посмотреть здесь.
Есть ещё одна интересный вопрос — а как удлинить массив в стеке? На первый взгляд проблема нерешаема: массив в архитектуре x86 растёт в сторону старших адресов, а стек — наоборот, в сторону младших адресов. Тогда надо «перевернуть» массив, чтобы он рос в обратном направлении! По факту элемент массива с индексом [n] станет на самом деле элементом с индексом [-n]. Этот массив может расти при условии, что он находится на вершине стека и за ним ничего нет. Если наращиванием массива будет заниматься какая-то вызываемая функция, то возникнет угроза перекрытия растущего массива и локальных данных этой функции. Так что для аккуратного программирования тут лучше опять использовать модель памяти с двумя стеками. Подробности — здесь. Ещё надо заметить, что массив — не единственный агрегатный тип данных, который может расти в стеке. Расти, наверное, могут многие, если не все — при условии правильного исполнения. Массив же выбран в качестве примера по причине его простоты.
Всё вышеперечисленное подчиняется дисциплине LIFO: буква «L» в этой аббревиатуре означает «Last», «последний». То есть и резервирование памяти с помощью alloca, и создание нового объекта конструктором в двухстековой модели, и удлинение массива — всё это делается в памяти, лежащей ниже последнего («Last») элемента стека.
И последнее: пока что мне не знакомы языки, в которых есть двухстековая модель памяти или удлинение массива на вершине стека. Надеюсь, мои замечания добавили недостающие штрихи к портрету всем известного стека.
Управление памятью в Swift