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

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

Sobes Copilot

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

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

sobes.tech AI

Утечка памяти (memory leak) — это ситуация, когда выделенная память больше не используется программой, но остается недоступной для сборщика мусора (или системы управления памятью), поскольку на нее продолжают ссылаться объекты. В iOS это происходит из-за циклических ссылок между объектами, которые используют Automatic Reference Counting (ARC).

Как возникает:

ARC в iOS автоматически управляет памятью, освобождая объекты, когда на них нет активных ссылок. Утечка возникает, когда два или более объекта ссылаются друг на друга сильными (по умолчанию) ссылками, создавая замкнутый цикл. В этом случае счетчик ссылок каждого объекта никогда не достигает нуля, даже если на эту группу объектов нет внешних ссылок. ARC не может освободить эти объекты.

Пример:

class Parent {
    var child: Child?
    // ...
    deinit {
        print("Parent deinitialized")
    }
}

class Child {
    var parent: Parent?
    // ...
    deinit {
        print("Child deinitialized")
    }
}

func createLeak() {
    let parent = Parent()
    let child = Child()

    parent.child = child
    child.parent = parent // Сильная циклическая ссылка
}

createLeak()
// parent и child будут существовать после выхода из функции,
// несмотря на то, что на них нет внешних ссылок
// Сообщения deinit не будут напечатаны

В данном примере parent сильно ссылается на child, а child сильно ссылается на parent. Это создает цикл. Когда функция createLeak завершается, локальные переменные parent и child выходят из области видимости, но сильные ссылки между экземплярами Parent и Child остаются, предотвращая их освобождение ARC.

Для предотвращения утечек памяти в iOS используются слабые (weak) и бесхозные (unowned) ссылки, которые не увеличивают счетчик ссылок объекта.

  • weak: Используется, когда срок жизни объектов может быть разным. Слабая ссылка может стать nil, если объект, на который она ссылается, будет освобожден.
  • unowned: Используется, когда срок жизни ссылающегося объекта не превышает срока жизни объекта, на который указывает бесхозная ссылка. Бесхозная ссылка всегда имеет значение и не может быть nil. Обращение к освобожденному объекту по unowned ссылке приведет к ошибке времени выполнения.
class Parent {
    var child: Child?
    // ...
    deinit {
        print("Parent deinitialized")
    }
}

class Child {
    weak var parent: Parent? // Используем слабую ссылку
    // ...
    deinit {
        print("Child deinitialized")
    }
}

func fixLeak() {
    let parent = Parent()
    let child = Child()

    parent.child = child
    child.parent = parent // Теперь связка родитель-потомок не содержит циклической сильной связи
}

fixLeak()
// parent и child будут освобождены после выхода из функции,
// поскольку циклическая сильная ссылка устранена
// Сообщения deinit будут напечатаны