← Назад к вопросам

Как работали с памятью до Side Table?

3.0 Senior🔥 81 комментариев
#Управление памятью#Язык Swift

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Краткий ответ: использование unowned(unsafe) и явных weak-ссылок

До появления Side Table в Swift (который появился с механизмом подсчёта ссылок Automatic Reference Counting, но эволюционировал) управление памятью в основном опиралось на явные weak-ссылки и unowned(unsafe) ссылки в комбинации с ручным управлением жизненным циклом объектов. В Objective-C и ранних версиях Swift использовалась схожая с Side Table концепция, но реализованная иначе — через дополнительные объекты-посредники или внешние таблицы в среде выполнения Objective-C.

Основные механизмы управления памятью

1. Objective-C Runtime и объекты weak-ссылок

В Objective-C до ARC (Automatic Reference Counting) weak-ссылки реализовывались через runtime-функции, которые хранили mapping между объектами и weak-указателями. Например:

// Пример ручного управления weak-ссылкой в Objective-C без ARC
__weak NSObject *weakRef = strongObject;
// Runtime хранил weakRef в отдельной хэш-таблице

Как это работало:

  • Среда выполнения Objective-C поддерживала глобальную хэш-таблицу, где ключом был адрес объекта, а значением — список weak-указателей.
  • При освобождении объекта runtime обходил этот список и занулял все weak-указатели (превращал их в nil).
  • Это требовало синхронизации (блокировок) для потокобезопасности, что могло снижать производительность.

2. Явные weak-ссылки с ручным управлением

В Swift до оптимизаций Side Table weak-ссылки были более "тяжёлыми". Например:

class MyClass {
    var name: String
    init(name: String) { self.name = name }
}

// Weak-ссылка создавалась через отдельный внутренний объект
weak var weakInstance: MyClass? = MyClass(name: "Test")
// Под капотом: Swift создавал промежуточный объект для отслеживания

Недостатки такого подхода:

  • Каждая weak-ссылка могла требовать отдельного объекта-обёртки, что увеличивало потребление памяти.
  • Производительность: операции установки/чтения weak-ссылки требовали дополнительных шагов (поиск в таблице, блокировки).
  • Потокобезопасность: требовались механизмы синхронизации, усложнявшие реализацию.

3. Использование unowned(unsafe) как альтернативы

Для случаев, когда объект гарантированно жив, использовали unowned(unsafe) — аналог unsafe_unretained в Objective-C. Это была просто сырая указатель без подсчёта ссылок:

class Parent {
    var child: Child?
}

class Child {
    unowned(unsafe) let parent: Parent // Небезопасно, но быстро
    init(parent: Parent) { self.parent = parent }
}

Риски:

  • Если объект освобождался, ссылка становилась висячей (dangling pointer), что могло привести к падению или повреждению памяти.
  • Использовалось только в критичных к производительности местах, где жизненный цикл был строго контролируем.

4. Ручное управление через указатели и CFType

Для низкоуровневых задач (например, взаимодействие с C API) память управлялась вручную:

// Пример с Core Foundation
let cfString = CFStringCreateWithCString(nil, "Hello", CFStringBuiltInEncodings.UTF8.rawValue)
// Требуется явное управление счетчиком ссылок
CFRetain(cfString)
CFRelease(cfString)

Сравнение с Side Table

До Side Table:

  • Weak-ссылки: реализованы через глобальные таблицы в runtime, что могло приводить к конфликтам блокировок.
  • Память: дополнительные накладные расходы на каждый weak-указатель.
  • Производительность: операции с weak-ссылками были медленнее из-за поиска в таблицах и синхронизации.

После Side Table в Swift:

  • Локализация: Side Table хранится рядом с объектом (инлайн в некоторых случаях), уменьшая накладные расходы.
  • Оптимизация: счётчик ссылок и weak-информация объединены, улучшая производительность.
  • Безопасность: меньше рисков висячих указателей благодаря интеграции с ARC.

Заключение

До Side Table управление памятью, особенно weak-ссылками, было менее эффективным и более сложным — оно полагалось на глобальные структуры данных в runtime, что могло создавать узкие места в многопоточных сценариях. Развитие ARC и Side Table в Swift позволило локализовать метаданные памяти, улучшив производительность и безопасность. Тем не менее, понимание старых подходов важно для работы с легаси-кодом или низкоуровневой оптимизацией.