Какие механизмы оптимизации есть у стека, особенно для коллекций в Swift
Что проверяют
Понимаете ли вы, где в Swift данные лежат на самом деле: что struct не всегда живёт на стеке, а Array при всей своей семантике значения хранит элементы в куче. Следом обычно спрашивают, как работает copy-on-write и когда он внезапно копирует целый массив.
Как отвечать
Разведите два уровня. Стек и компилятор:
- value types: локальные структуры и перечисления без ссылочных полей обычно лежат на стеке или в регистрах, без ARC и выделения памяти;
- продвижение на стек: оптимизатор может разместить на стеке даже экземпляр класса или замыкание, если доказал, что они не покидают функцию. Помогают
finalиprivate, открывающие девиртуализацию и инлайнинг, и сборка с-Oи whole module optimization; InlineArray: в Swift 6.2 появился массив фиксированного размера, который хранит элементы прямо внутри значения, без буфера в куче. Там жеSpanдля безопасного доступа к непрерывной памяти без копий.
Коллекции:
- copy-on-write:
Array,Dictionary,SetиStringделят буфер между копиями до первой записи. В своих типах это повторяют черезisKnownUniquelyReferenced; reserveCapacity: если размер известен заранее, массив не будет перевыделять буфер по мере роста;ContiguousArray: гарантирует непрерывное хранение без моста кNSArray, выигрыш заметен, когда элементы это классы или протоколы;.lazy: цепочкаmapиfilterбез промежуточных массивов;withUnsafeBufferPointer: доступ к элементам без проверок границ в горячем цикле, когда профилирование показало узкое место.
Любое такое решение подтверждайте замером в Instruments (Time Profiler, Allocations), а не интуицией.
Где спотыкаются
Говорят «структуры всегда на стеке». Структура с Array или String внутри несёт ссылку на буфер в куче и участвует в ARC.
Мутируют массив через временную копию в цикле: var tmp = model.items; tmp.append(x); model.items = tmp. Пока старое значение живо в свойстве, каждый append копирует весь буфер, и O(n) превращается в O(n²).
Утверждают, что map и filter быстрее цикла. Цепочка без .lazy создаёт промежуточный массив на каждом шаге, и обычный цикл часто выигрывает.