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

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

Sobes Copilot

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

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

sobes.tech AI

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

Основные причины утечек памяти связаны с циклами сильных ссылок (Strong Reference Cycles), когда два или более объекта сильно ссылаются друг на друга, предотвращая их освобождение, даже если к ним больше нет доступа извне.

Для предотвращения утечек памяти используются:

  • Automatic Reference Counting (ARC): Автоматическое управление памятью в Swift и Objective-C, которое отслеживает и управляет временем жизни объектов, автоматически освобождая память, когда strong reference count объекта становится равным нулю.

  • weak и unowned ключевые слова:

    • weak используется для ссылок, которые не должны препятствовать освобождению объекта, на который они ссылаются. Ссылка становится nil, когда объект освобождается. Используется для предотвращения циклов сильных ссылок между объектами, где один объект может существовать без другого, или когда связь является опциональной.
    • unowned также не удерживает сильной ссылки на объект, но в отличие от weak, предполагается, что ссылающийся объект будет иметь такое же или более короткое время жизни, чем объект, на который ссылаются. unowned ссылка не опциональна и не становится nil. Если объект, на который ссылаются, освобождается до того, как unowned ссылка будет использована, это приведет к крашу.

Примеры использования weak и unowned в циклах замыканий:

// Пример цикла сильных ссылок с замыканием
class Person {
    let 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
    var tenant: Person?

    init(unit: String) {
        self.unit = unit
        print("Apartment \(unit) is being initialized")
    }

    deinit {
        print("Apartment \(unit) is being deinitialized")
    }
}

var john: Person? = Person(name: "John Appleseed")
var unit4A: Apartment? = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john

john = nil // deinit не вызван
unit4A = nil // deinit не вызван
// Произошла утечка памяти из-за цикла сильных ссылок

// Решение с использованием weak или unowned
class PersonFixed {
    let name: String
    var apartment: ApartmentFixed?

    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }

    deinit {
        print("\(name) is being deinitialized")
    }
}

class ApartmentFixed {
    let unit: String
    weak var tenant: PersonFixed? // Использование weak для разрыва цикла

    init(unit: String) {
        self.unit = unit
        print("Apartment \(unit) is being initialized")
    }

    deinit {
        print("Apartment \(unit) is being deinitialized")
    }
}

var johnFixed: PersonFixed? = PersonFixed(name: "John Appleseed")
var unit4AFixed: ApartmentFixed? = ApartmentFixed(unit: "4A")

johnFixed!.apartment = unit4AFixed
unit4AFixed!.tenant = johnFixed

johnFixed = nil // deinit для PersonFixed вызван
unit4AFixed = nil // deinit для ApartmentFixed вызван
// Память освобождена

В замыканиях cycle reference решается использованием capture list:

class HTMLElement {
    let name: String
    let text: String?

    lazy var asHTML: () -> String = {
        [unowned self] in // Использование unowned в capture list
        if let text = self.text {
            return "<\(self.name)> \(text) </\(self.name)>"
        } else {
            return "<\(self.name)>"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) is being deinitialized")
    }
}

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())

paragraph = nil // deinit вызван

Инструменты для выявления утечек памяти включают:

  • Instruments (Leaks): Мощный инструмент в Xcode Profile, который отслеживает выделенную и освобожденную память, показывая объекты, которые должны были быть освобождены, но не были.
  • Memory Graph Debugger: Встроенный в Xcode отладчик графа памяти, позволяющий визуализировать взаимосвязи между объектами и находить циклы сильных ссылок.
  • Static Analyzer: В Xcode анализирует код и может предупредить о потенциальных утечках памяти и других логических ошибках.