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

Почему возникает утечка памяти?

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

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

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

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

Причины утечек памяти (Memory Leaks) в iOS-разработке

Утечка памяти возникает, когда объекты продолжают занимать память, хотя они уже не используются приложением и на них отсутствуют сильные ссылки (strong references). В контексте iOS-разработки на Swift/Objective-C это обычно связано с неправильным управлением жизненным циклом объектов и нарушением принципов ARC (Automatic Reference Counting).

Основные механизмы возникновения утечек

1. Strong Reference Cycles (Циклы сильных ссылок)

Наиболее распространённая причина — взаимное удержание объектов через сильные ссылки:

class User {
    var device: Device?
}

class Device {
    var owner: User?
}

func createLeak() {
    let user = User()
    let device = Device()
    
    user.device = device  // Сильная ссылка на Device
    device.owner = user   // Сильная ссылка на User
    // После выхода из функции оба объекта остаются в памяти
    // Они взаимно удерживают друг друга через strong references
}

2. Захват self в замыканиях

Некорректное использование self внутри замыканий:

class DataManager {
    var data: [String] = []
    var completion: (() -> Void)?
    
    func setupHandler() {
        // УТЕЧКА: сильное захватывание self
        completion = {
            self.processData()  // Сильная ссылка на self
        }
    }
    
    func processData() {
        print(data.count)
    }
}

3. Использование делегатов как сильных ссылок

Нарушение принципа "делегат должен быть weak":

protocol ServiceDelegate: AnyObject {
    func didReceiveData()
}

class Service {
    var delegate: ServiceDelegate?  // ДОЛЖНО БЫТЬ weak!
    
    func fetchData() {
        // После завершения работы объект делегата не освободится
    }
}

Типичные сценарии утечек

Retain Cycles в замыканиях

class ViewController: UIViewController {
    var timer: Timer?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Утечка: timer сохраняет strong reference к self
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
            self.updateUI()  // Сильное захватывание self
        }
    }
    
    // Решение: использовать weak или unowned
    func safeTimerSetup() {
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
            guard let self = self else { return }
            self.updateUI()
        }
    }
}

Циклы в иерархии объектов

class Parent {
    var child: Child?
}

class Child {
    var parent: Parent?  // Должно быть weak или unowned
}

Техники обнаружения утечек

  1. Инструменты Xcode:

    • Memory Graph Debugger — визуализация объектов в памяти
    • Leaks Instrument — автоматическое обнаружение утечек
    • Allocations Instrument — анализ выделения памяти
  2. Логирование жизненного цикла:

class ObservableObject {
    deinit {
        print("✅ Объект освобождён")  // Если не видим — есть утечка
    }
}

Профилактика и лучшие практики

Использование weak и unowned:

class NetworkManager {
    weak var delegate: NetworkDelegate?
    
    func fetch(completion: @escaping (Result<Data, Error>) -> Void) {
        URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
            guard let self = self else { return }
            // Безопасная работа с self
            self.process(data: data)
            completion(.success(data))
        }.resume()
    }
}

Паттерны для избежания циклов:

  • Использование value types (Struct, Enum) где возможно
  • Соблюдение правил владения в архитектуре (MVVM, VIPER)
  • Аккуратное использование singleton'ов и глобальных объектов

Последствия утечек памяти

  1. Постепенное увеличение потребления памяти (Memory Growth)
  2. Предупреждения системы (Memory Warnings)
  3. Принудительное завершение приложения (Crash due to OOM — Out Of Memory)
  4. Деградация производительности из-за частых сборок мусора
  5. Перегрев устройства и быстрая разрядка батареи

Критически важные моменты:

  • Все delegate, dataSource и обратные вызовы должны быть weak
  • Замыкания, захватывающие self, должны использовать [weak self] или [unowned self]
  • Особое внимание к объектам с длительным жизненным циклом (сервисы, менеджеры)
  • Регулярное профилирование приложения с Memory Graph Debugger

Правильное управление памятью — фундаментальный навык iOS-разработчика, напрямую влияющий на стабильность и пользовательский опыт приложения. Современные инструменты Xcode значительно упрощают обнаружение утечек, но понимание механизмов ARC остаётся обязательным для написания надежного кода.