> Почему при взятии подмассива массива возвращается ArraySlice, а при взятии подстроки из String возвращается Substring (iOS, Swift)
Уровень: senior · Роль: mobile · Категория: Технические вопросы
Компании: Яндекс
Стек: iOS, Swift
> Пример ответа
Короткий ответ
ArraySlice и Substring - это легковесные view поверх исходной коллекции, которые разделяют память с оригиналом. Они возвращаются вместо новых копий, чтобы избежать лишних аллокаций и копирований при частых операциях нарезки. Разные имена подчёркивают, что это разные типы с разными контрактами: Substring не является String, а ArraySlice не является Array, хотя оба сохраняют большинство API родительского типа.
Подробное объяснение
В Swift строки и массивы - это value types с семантикой копирования при записи (copy-on-write). Если бы prefix, suffix или dropFirst возвращали новый Array или String, каждая операция требовала бы полного копирования элементов. Для больших коллекций это дорого.
Вместо этого Swift возвращает ArraySlice и Substring - структуры, которые хранят ссылку на исходный буфер памяти и диапазон индексов. Создание такого view - O(1) по времени и памяти.
Почему не один общий тип? Потому что:
Substringдолжен поддерживатьStringProtocol, чтобы можно было передавать его в функции, ожидающиеString(через generic constraint).ArraySliceдолжен сохранять индексную семантикуArray(Int-based), но с возможным ненулевым начальным индексом.- Разделение типов позволяет компилятору точнее оптимизировать и предотвращает случайное смешение разных коллекций.
Важно: Substring и ArraySlice живут только пока жив оригинал. Если оригинал освобождается, view всё равно удерживает буфер, поэтому длительное хранение среза может удерживать всю память оригинала.
На практике
- Не храните
SubstringилиArraySliceдолго - если нужна постоянная строка/массив, конвертируйте явно:String(substring),Array(slice). - Индексы
ArraySliceне обязательно начинаются с 0:array[2...4]даст slice сstartIndex == 2. - Для
Substringиндексы совпадают с индексами исходнойString, но тип индекса -String.Index, а неInt. - Операции над slice (например,
map,filter) возвращают уже обычныйArray, потому что результат не является view. - При передаче slice в функцию, ожидающую
Array, нужна явная конвертация - это защита от случайного удержания памяти.
Пример кода
SWIFTlet array = [1, 2, 3, 4, 5]let slice = array[1...3]print(type(of: slice)) // ArraySlice<Int>print(slice.startIndex) // 1print(Array(slice)) // [2, 3, 4]let str = "Hello, world"let sub = str.prefix(5)print(type(of: sub)) // Substringprint(String(sub)) // "Hello"// Долгое хранение slice удерживает весь массивvar bigArray = Array(0..<100_000)let smallSlice = bigArray[0..<10]bigArray = [] // память всё ещё занята, пока жив smallSlice
Как отвечать на собеседовании
Начните с ключевой идеи: это оптимизация производительности через copy-on-write и view поверх буфера. Затем объясните разницу контрактов: Substring - это StringProtocol, ArraySlice - RandomAccessCollection. Упомяните, что оба типа - value types, но разделяют память с оригиналом. Подчеркните практическое следствие: не хранить срезы долго, конвертировать при необходимости. Если спросят про индексы - упомяните ненулевой startIndex у ArraySlice.
Что проверяет интервьюер
- Понимание модели памяти Swift: value types, copy-on-write, буферы.
- Знание стандартной библиотеки:
ArraySlice,Substring,StringProtocol. - Умение объяснять trade-off между производительностью и удобством API.
- Осознание опасности удержания памяти через срезы.
- Понимание, почему дизайн разделяет типы, а не использует один универсальный.
Типичные ошибки
- Утверждение, что
Substring- это подклассString(это не так, это отдельная структура). - Забывают, что
ArraySliceможет иметь ненулевойstartIndex. - Говорят, что срез копирует данные - это неверно, копирование происходит только при изменении (copy-on-write).
- Не упоминают, что срез удерживает весь оригинальный буфер, а не только свои элементы.
- Путают
SubstringсString.Index- это разные вещи:Substring- коллекция,String.Index- позиция.
> Похожие задачи по mobile
Что означает ошибка компиляции при отсутствии атрибута escaping?
Можно ли объединить массивы через reduce
Как реализовать unit test для функции getValue
Какой протокол нужно реализовать для сохранения файла в iOS?
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью