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

Какие знаешь типы утечек памяти в Swift?

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

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

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

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

Типы утечек памяти в Swift

В Swift, несмотря на наличие автоматического управления памятью через ARC (Automatic Reference Counting), утечки памяти все же возможны. Основные типы утечек связаны с циклическими ссылками, некорректным использованием замыканий (closures), удержанием strong ссылок в неподходящих контекстах и ошибками при работе с C-библиотеками или unmanaged объектами.

1. Циклические ссылки (Retain Cycles)

Самая распространённая причина утечек — когда два или более объекта держат strong ссылки друг на друга, создавая замкнутый цикл, который ARC не может разорвать.

Пример циклической ссылки между классами:

class Person {
    var name: String
    var laptop: Laptop?
    
    init(name: String) {
        self.name = name
    }
}

class Laptop {
    var model: String
    var owner: Person?
    
    init(model: String) {
        self.model = model
    }
}

var john: Person? = Person(name: "John")
var macbook: Laptop? = Laptop(model: "MacBook Pro")

john?.laptop = macbook
macbook?.owner = john // Циклическая strong ссылка!

john = nil
macbook = nil // Объекты НЕ будут освобождены!

Для решения используются weak или unowned ссылки:

class Laptop {
    var model: String
    weak var owner: Person? // weak ссылка!
    
    init(model: String) {
        self.model = model
    }
}

2. Утечки в замыканиях (Closures)

Замыкания захватывают ссылки на объекты, и если объект хранит strong ссылку на замыкание, которое захватывает сам объект, возникает цикл.

Пример утечки в замыкании:

class DataManager {
    var data: [String] = []
    var handler: (() -> Void)?
    
    func setupHandler() {
        handler = {
            // self захвачен strong ссылкой!
            self.processData()
        }
    }
    
    func processData() {
        print("Processing \(data.count) items")
    }
}

Решение — использовать capture list с weak или unowned:

func setupHandler() {
    handler = { [weak self] in
        guard let self = self else { return }
        self.processData()
    }
}

3. Strong ссылки в делегатах (Delegate Cycles)

Частая ошибка — объявление делегата как strong свойства вместо weak. Это создаёт цикл между объектом и его делегатом.

Правильное объявление делегата:

protocol ServiceDelegate: AnyObject {
    func didReceiveData()
}

class Service {
    weak var delegate: ServiceDelegate? // weak обязателен!
}

4. Утечки при работе с C-библиотеками и unmanaged кодом

При использовании Core Foundation, C-библиотек или Unmanaged объектов, управление памятью может быть ручным. Неправильное использование Unmanaged или отсутствие вызовов CFRelease приводят к утечкам.

Пример с Core Foundation:

let cfString = CFStringCreateWithCString(nil, "Hello", CFStringEncoding.utf8)
// Необходимо явное освобождение:
CFRelease(cfString)

5. Утечки в наблюдателях (Observers) и NotificationCenter

Не удаление наблюдателей из NotificationCenter или KVO при освобождении объекта приводит к утечкам, так центр уведомлений держит strong ссылки на наблюдателей.

class Observer {
    init() {
        NotificationCenter.default.addObserver(self, selector: #selector(handleEvent), name: .someEvent, object: nil)
    }
    
    @objc func handleEvent() { }
    
    deinit {
        // ОБЯЗАТЕЛЬНО удаляем наблюдателя!
        NotificationCenter.default.removeObserver(self)
    }
}

6. Утечки при использовании таймеров (Timer)

Strong ссылки таймеров на целевые объекты могут создавать циклы. Например, Timer создает strong ссылку на свой target.

Решение — использовать блоки вместо target-action:

Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] timer in
    self?.update()
}

Заключение

Основные методы предотвращения утечек:

  • Анализ графа объектов: понимание, кто владеет ссылками.
  • Использование weak и unowned: где возможны циклы.
  • Инструменты профилирования: Xcode Memory Graph Debugger и Instruments (Leaks).
  • Паттерны проектирования: избегание strong ссылок в делегатах, замыканиях.

Swift и ARC эффективны, но требуют внимания разработчика к циклическим зависимостям и захвату ссылок. Регулярный анализ памяти в Instruments и использование Memory Graph Debugger — ключевые практики для стабильных приложений.