В чем разница между перебором массива и строки в Swift?

«В чем разница между перебором массива и строки в Swift?» — вопрос из категории Swift Core, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Хотя и Array, и String соответствуют протоколу Sequence, их внутреннее устройство и семантика итерации различаются.

Перебор массива (Array)

Массив — это коллекция элементов с целочисленной индексацией (O(1) доступ).

let numbers: [Int] = [10, 20, 30]

// 1. Цикл for-in
for number in numbers {
    print(number) // 10, 20, 30
}

// 2. Прямой доступ по индексу
for i in 0..<numbers.count {
    print(numbers[i]) // 10, 20, 30
}

Перебор строки (String)

Строка — это коллекция расширенных графемных кластеров (Character). Доступ по индексу не является O(1) из-за Unicode.

let greeting = "Hello🇺🇦"

// 1. Цикл for-in (перебор Character)
for character in greeting {
    print(character) // H, e, l, l, o, 🇺🇦 (флаг как один Character)
}

// 2. Использование индексов String.Index
for index in greeting.indices {
    print(greeting[index]) // Аналогичный вывод
}

// НЕВЕРНО: print(greeting[0]) - Ошибка компиляции

Сравнительная таблица

Аспект Массив (Array<T>) Строка (String)
Тип элемента Любой тип T Character (графемный кластер)
Индексация Int (быстрая, O(1)) String.Index (относительная позиция)
Изменяемость Элементы можно менять по индексу Строка — value type, изменение создает копию
Сложность итерации O(n) O(n), но с большей константой из-за Unicode

Почему так? Строка должна корректно обрабатывать составные символы (например, "é" = e + ́ ) и эмодзи (например, "👨‍👩‍👧‍👦"), поэтому прямой доступ по целочисленному индексу невозможен.