Добавить объявление

Массивы в Swift: больше чем кажется на первый взгляд

Друзья, сегодня поговорим о массивах в Swift. Казалось бы, самой базовой структуре данных, но с интересными особенностями под капотом.

Value type но со своими хитростями:


Массивы - это структуры (value types), но данные хранятся в куче. При присваивании в переменную создается новая структура, но буфер с данными остается общим до момента модификации (Copy-on-Write):

var array1 = [1, 2, 3]
var array2 = array1 // Пока нет копирования данных
array1.append(4) // Только теперь данные копируются

Работа с ссылочными типами:


С классами нужно быть аккуратнее:

class User {
var name: String

init(name: String) {
self.name = name
}
}

let users1 = [User(name: "Artem"), User(name: "Dima")]
let users2 = users1

users1[0].name = "Egor" // Изменение затронет оба массива
print(users2[0].name) // Egor

Для глубокого копирования нужно реализовать протокол NSCopying. Без него при копировании массива с классами создаются только новые ссылки на те же объекты в памяти. NSCopying позволяет создать настоящие копии каждого объекта.

Производительность операций с массивами:


На практике важно понимать сложность основных операций с массивами, чтобы выбирать оптимальные подходы:

  • append() - в среднем O(1), но при расширении буфера происходит копирование всех элементов.

  • removeLast() - O(1), так как не требует сдвига элементов.

  • remove(at:) - O(n) из-за необходимости сдвигать все последующие элементы.

  • insert(_:at:) - O(n) по той же причине, что и remove(at:).

Эффективная работа с частями массива (ArraySlice):


ArraySlice работает как указатель на определенный диапазон элементов в оригинальном массиве. Это особенно полезно при работе с большими массивами:

let largeArray = Array(0..<1000000) // Большой массив
let slice = largeArray[1000..<2000] // Не копирует миллион элементов!

// Используем срез как обычный массив
for element in slice {
print(element) // Работает с элементами 1000-1999
}

Но важно помнить: ArraySlice сохраняет сильную ссылку на весь исходный массив. Это может привести к неожиданному потреблению памяти. Вот решение:

let largeArray = Array(0..<1000000) // Большой массив
let slice = largeArray[1000..<2000] // Не копирует миллион элементов!

// Создаем новый массив если нужна независимая копия
let independentCopy = Array(slice) // Теперь largeArray можно освободить

Массивы под капотом:


Swift использует разные буферы в зависимости от контекста:

  • _ContiguousArrayBuffer - для Swift-типов.

  • _ArrayBuffer - при работе с Objective-C.

Вывод:


Массивы в Swift - это не просто списки значений. Понимание их устройства помогает писать более эффективный код и избегать неочевидных проблем.
15.10.2025 10 473