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

Где хранятся Weak ссылки?

2.0 Middle🔥 142 комментариев
#Управление памятью

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

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

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

Хранение Weak ссылок в iOS/macOS (Objective-C/Swift)

Weak ссылки в iOS/macOS экосистеме хранятся в специальной глобальной хеш-таблице, известной как Side Table или weak_table_t, которая является частью системы управления памятью Objective-C Runtime. Это не просто массив, а сложная структура данных, оптимизированная для быстрого поиска и очистки.

Механизм работы Weak ссылок

Когда объекту присваивается weak-ссылка, Runtime выполняет следующие шаги:

  1. Регистрация в Weak Table: Создается запись в глобальной weak-таблице (weak_table_t), которая связывает адрес исходного объекта (referent) со списком всех weak-указателей (referrers) на него.
  2. Отслеживание деаллокации: При деаллокации объекта Runtime ищет все weak-ссылки на него в этой таблице.
  3. Автоматическое обнуление (Zeroing): Все найденные weak-ссылки автоматически устанавливаются в nil.

Структура weak_table_t

Рассмотрим ключевые компоненты на уровне Runtime (Objective-C):

struct weak_table_t {
    weak_entry_t *weak_entries;      // Массив записей
    size_t    num_entries;           // Текущее количество записей
    uintptr_t mask;                  // Маска для хеширования
    uintptr_t max_hash_displacement; // Максимальное смещение при коллизиях
};

struct weak_entry_t {
    DisguisedPtr<objc_object> referent; // Замаскированный указатель на объект
    union {
        struct {
            weak_referrer_t *referrers; // Массив weak-ссылок
            uintptr_t        out_of_line_ness : 2;
            uintptr_t        num_refs : PTR_MINUS_2;
            uintptr_t        mask;
            uintptr_t        max_hash_displacement;
        };
        struct {
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT]; // Встроенный массив для малого количества ссылок
        };
    };
};

Ключевые особенности:

  • Динамическое расширение: При небольшом количестве weak-ссылок используется inline-массив (обычно на 4 элемента), что избегает дополнительных аллокаций.
  • Хеш-таблица для коллизий: При превышении лимита создается отдельная хеш-таблица (referrers).
  • Замаскированные указатели: Адреса объектов хранятся в "замаскированном" виде для безопасности.

Пример использования в Swift

class MyClass {
    var value: Int
    init(value: Int) { self.value = value }
    deinit { print("MyClass \(value) deallocated") }
}

var strongObject: MyClass? = MyClass(value: 42)
weak var weakReference = strongObject  // Регистрация в weak_table_t

print(weakReference?.value) // Optional(42)

strongObject = nil          // Объект деаллоцируется
// В деаллокаторе Runtime находит weak-ссылку в таблице и обнуляет её

print(weakReference)        // nil
// Запись очищается из weak_table_t при следующем обслуживании таблицы

Жизненный цикл weak-ссылки

  1. Инициализация:

    weak var ref = object // Runtime добавляет запись в weak_table_t
    
  2. Использование с безопасностью:

    if let strongRef = weakRef { // Временное усиление (strong)
        // Работа с объектом
    }
    
  3. Деаллокация объекта:

    • Runtime вызывает objc_destructInstance()
    • В object_dispose() проверяется weak-таблица
    • Все weak-ссылки устанавливаются в nil
    • Запись помечается для очистки
  4. Очистка таблицы: При последующих операциях с weak-таблицей "мертвые" записи удаляются.

Оптимизации и особенности реализации

  • Thread-safe доступ: Weak-таблица защищена глобальной блокировкой (lock) или использует атомарные операции для потокобезопасности.
  • Autorelease оптимизация: Система старается сохранять weak-ссылки в autorelease pool для предотвращения преждевременной деаллокации.
  • ARC не генерирует код для weak-хранилища: Вся магия происходит на уровне Runtime, компилятор лишь вставляет вызовы соответствующих функций.
  • Производительность: Операции с weak-ссылками дороже, чем с strong, из-за необходимости синхронизации и работы с глобальной структурой.

Практические рекомендации

// ✅ Правильное использование
class ViewController: UIViewController {
    weak var delegate: MyDelegate? // Избегаем retain cycles
    
    func configure() {
        // Всегда используем optional binding
        guard let delegate = delegate else { return }
        delegate.didUpdateData()
    }
}

// ❌ Проблемный паттерн
var weakArray: [Weak<MyClass>] = [] // Самодельные weak-коллекции
// Лучше использовать NSHashTable или NSPointerArray
let weakContainer = NSHashTable<AnyObject>.weakObjects()

Итог: Weak-ссылки хранятся в глобальной thread-safe хеш-таблице Runtime, которая обеспечивает их автоматическое обнуление при деаллокации объектов. Эта система является фундаментальной частью ARC, позволяя избегать retain cycles при сохранении безопасности памяти. Понимание внутреннего устройства weak-таблицы помогает писать более эффективный и надежный код, особенно в многопоточных средах и при работе с сложными графами объектов.