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

Какие типы можно использовать с weak?

1.0 Junior🔥 162 комментариев
#Управление памятью

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

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

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

Типы, совместимые с weak в Swift

В Swift модификатор weak применяется исключительно к ссылочным типам (reference types), поскольку он управляет подсчётом ссылок в системе автоматического управления памятью (ARC). weak создаёт не-владеющую (non-owning) ссылку, которая не увеличивает счётчик ссылок объекта, и автоматически становится nil, когда объект освобождается.

Основные требования для использования weak

  1. Тип должен быть классом (или экземпляром класса) – weak неприменим к структурам (struct), перечислениям (enum) или примитивным типам, так как они являются типами-значениями (value types) и не используют подсчёт ссылок.
  2. Тип должен быть опциональнымweak ссылки всегда опциональны (Optional), потому что ARC может обнулить их в любой момент.
  3. Тип должен поддерживать нулевые значения – явно или неявно, через опциональность.

Конкретные типы, которые можно использовать с weak

1. Пользовательские классы

Любой класс, определённый разработчиком, совместим с weak. Это самый частый случай использования для предотвращения циклов сильных ссылок (strong reference cycles).

class User {
    var name: String
    weak var subscription: Subscription? // weak к другому классу
    
    init(name: String) {
        self.name = name
    }
}

class Subscription {
    var type: String
    var user: User? // Сильная ссылка (может создать цикл)
    
    init(type: String) {
        self.type = type
    }
}

2. Подклассы NSObject

Классы, наследуемые от NSObject (включая большинство классов UIKit/AppKit), также поддерживают weak. Пример с UIViewController:

class ProfileViewController: UIViewController {
    weak var delegate: ProfileDelegate? // weak на протокол, который ограничен классом
}

protocol ProfileDelegate: AnyObject { // Протокол, ограниченный классом
    func didUpdateProfile()
}

3. Протоколы, ограниченные классами (AnyObject)

Протоколы с ограничением : AnyObject или : class (устаревший синтаксис) могут использоваться с weak, так как гарантируют, что только классы их реализуют.

protocol DataLoaderDelegate: AnyObject {
    func dataDidLoad(_ data: Data)
}

class DataManager {
    weak var delegate: DataLoaderDelegate? // Корректно, т.к. протокол ограничен классом
}

Типы, которые НЕЛЬЗЯ использовать с weak

  1. Структуры (struct) – вызывают ошибку компиляции:

    struct Point { var x, y: Double }
    // weak var point: Point? // Ошибка: 'weak' cannot be applied to non-class type 'Point'
    
  2. Перечисления (enum) – аналогично структурам:

    enum Direction { case north, south }
    // weak var direction: Direction? // Ошибка
    
  3. Примитивные типы (Int, String, Bool, Double, etc.) – являются типами-значениями.

  4. Протоколы без ограничения AnyObject – если протокол может быть реализован структурой, weak неприменим:

    protocol Drawable { } // Может быть struct или class
    // weak var drawable: Drawable? // Ошибка: 'weak' cannot be applied to non-class type 'Drawable'
    

Практические примеры использования weak

Предотвращение циклов сильных ссылок в замыканиях

Для захвата self в замыканиях без создания цикла:

class DataFetcher {
    var onUpdate: (() -> Void)?
    
    func fetchData() {
        let task = URLSession.shared.dataTask(with: someURL) { [weak self] data, _, _ in
            guard let self = self else { return } // Проверка на существование
            self.processData(data)
        }
        task.resume()
    }
    
    private func processData(_ data: Data?) { }
}

Делегаты и паттерн Delegate

Наиболее распространённый случай – weak ссылки на делегатов:

class NetworkManager {
    weak var delegate: NetworkManagerDelegate?
    
    func fetch() {
        // ... получение данных
        delegate?.didReceiveData(data) // Безопасный вызов
    }
}

Двунаправленные связи между объектами

При взаимных ссылках между объектами один из них должен использовать weak:

class Parent {
    var child: Child?
}

class Child {
    weak var parent: Parent? // weak разрывает потенциальный цикл
}

Особенности поведения weak ссылок

  • Автоматическое обнуление – когда объект освобождается, все weak ссылки на него становятся nil.
  • Не являются thread-safe – обращение к weak ссылке должно происходить в потоке, где существует объект, или с синхронизацией.
  • Нулевые значения – всегда проверяйте weak ссылки перед использованием через if let или guard let.
  • Производительность – weak ссылки требуют дополнительных накладных расходов, так как ARC хранит их в отдельной таблице для отслеживания.

Альтернативы weak

В некоторых случаях вместо weak можно использовать:

  • unowned – когда время жизни объекта гарантированно превышает время жизни ссылки (например, дочерний объект, который не может существовать без родителя).
  • Strong-weak dance – паттерн для захвата weak ссылок в замыканиях с последующим преобразованием в strong.
class Processor {
    func process(completion: @escaping () -> Void) {
        someAsyncOperation { [weak self] in
            guard let self = self else { return }
            // Здесь self - сильная ссылка, но только если объект ещё существует
            self.finalize()
            completion()
        }
    }
}

Заключение

weak в Swift применяется только к ссылочным типам: пользовательским классам, подклассам NSObject и протоколам с ограничением AnyObject. Его основное назначение – предотвращение циклов сильных ссылок в ситуациях с взаимными ссылками между объектами, в делегатах и замыканиях. Правильное использование weak критически важно для управления памятью в iOS-приложениях и предотвращения утечек памяти. При работе с weak всегда учитывайте их опциональную природу и проверяйте существование объекта перед обращением.