Назад к вопросам
Junior
91
questionbank

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

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Утечка памяти (memory leak) — это ситуация, когда выделенная память более недоступна для использования программой (потеряны все ссылки на нее), но при этом она не была освобождена операционной системой.

Причины возникновения утечки памяти в iOS:

  1. Циклические сильные ссылки (Retain Cycles): Два или более объекта ссылаются друг на друга сильными ссылками, предотвращая их деаллокацию, даже если на них больше нет внешних ссылок.

    class Person {
        var name: String
        var apartment: Apartment?
    
        init(name: String) {
            self.name = name
            print("\(name) is being initialized")
        }
    
        deinit {
            print("\(name) is being deinitialized")
        }
    }
    
    class Apartment {
        let unit: String
        weak var tenant: Person? // weak ссылка предотвращает цикл
    
        init(unit: String) {
            self.unit = unit
            print("Apartment \(unit) is being initialized")
        }
    
        deinit {
            print("Apartment \(unit) is being deinitialized")
        }
    }
    // Пример создания цикла сильных ссылок (если бы tenant был强 ссылкой)
    // var john: Person? = Person(name: "John Appleseed")
    // var unit4A: Apartment? = Apartment(unit: "4A")
    // john?.apartment = unit4A
    // unit4A?.tenant = john // strong reference here would cause a cycle
    
  2. Неправильное использование замыканий (Closures): Замыкание захватывает сильную ссылку на внешний объект (например, self), который владеет этим замыканием.

    class ViewController: UIViewController {
        var networkRequestCompletionHandler: (() -> Void)?
    
        func fetchData() {
            // Если бы здесь использовался явный self без capture list:
            // networkRequestCompletionHandler = {
            //     self.updateUI() // self is strongly captured
            // }
            // Правильно с capture list:
            networkRequestCompletionHandler = { [weak self] in
                self?.updateUI() // self is weakly captured
            }
        }
    
        func updateUI() {
            // update UI
        }
    
        deinit {
            print("ViewController is being deinitialized")
        }
    }
    

    Использование [weak self] или [unowned self] (unowned для случаев, когда объект, захватываемый замыканием, гарантированно живет дольше замыкания) предотвращает сильное захватывание.

  3. Отсутствие отписки от наблюдателей/подписок: Объекты, подписанные на уведомления (NotificationCenter), KVO или другие механизмы обратного вызова, могут утекать, если они не отписываются во время деаллокации.

    class ObserverClass {
        init() {
            NotificationCenter.default.addObserver(self, selector: #selector(handleNotification), name: Notification.Name("MyNotification"), object: nil)
        }
    
        @objc func handleNotification() {
            // handle notification
        }
    
        deinit {
            // Необходимо отписаться, иначе экземпляр может утечь
            NotificationCenter.default.removeObserver(self)
            print("ObserverClass is being deinitialized")
        }
    }
    
  4. Удержание ссылок в статических или глобальных переменных: Если объект удерживается сильной ссылкой из статической или глобальной переменной, он никогда не будет деаллоцирован.

  5. Неправильное использование C-указателей и Core Foundation объектов: Manual memory management required when bridging Swift/Objective-C with C APIs or Core Foundation objects. Incorrect handling of retain, release, free functions can lead to leaks or crashes.

Утечки памяти со временем приводят к исчерпанию доступной памяти приложения, что может вызвать замедление работы или аварийное завершение программы из-за нехватки ресурсов.