Память
October 16

Основная проблема ARC: цикл сильных ссылок

👋 В предыдущих постах мы уже рассматривали механизм ARC и то, как он работает на практике. Казалось бы, какие могут быть здесь проблемы?

⚠️ Однако существует риск написать код, в котором экземпляр класса никогда не достигнет состояния, когда у него не останется сильных ссылок. Это может произойти, если два экземпляра будут содержать сильные ссылки друг на друга, и каждый из них будет поддерживать работу другого. Такая известная проблема называется циклом сильных ссылок.

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

🚘 Возьмем класс Car и добавим к нему еще одно опциональное свойство owner — владельца машины.

class Car {
    let brand: String
    var owner: Owner?
    
    init(brand: String) {
        self.brand = brand
        print("\(brand) is being initialized")
    }
    
    deinit {
        print("\(brand) is being deinitialized")
    }
}

😎 Также создадим класс Owner с опциональным свойством car:

class Owner {
    let name: String
    var car: Car?
    
    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }
    
    deinit {
        print("\(name) is being deinitialized")
    }
}

🔷 Каждый экземпляр Car обладает свойством brand типа String и опциональным свойством owner. Это означает, что машина может не иметь владельца, и свойство owner является необязательным.

🔶 Аналогично, класс Owner имеет свойство name типа String и опциональное свойство car, поскольку у владельца может не быть автомобиля.

2️⃣ В обоих классах я добавил функции print, чтобы можно было отслеживать создание и освобождение объектов из памяти.

2️⃣ Теперь определим две переменные класса опционального типа:

var volvo: Car?
var artem: Owner?

🔷 Затем создадим конкретные экземпляры:

volvo = Car(brand: "Volvo")
artem = Owner(name: "Artem")

🔗 У двух созданных объектов по одной ссылке.

✨ Теперь присвоим объекту volvo хозяина artem, а у artem укажем машину volvo:

volvo?.owner = artem
artem?.car = volvo

🔗 Теперь у объектов по две ссылки.

🔶 Давайте освободим экземпляры из памяти и посмотрим, что произойдет:

volvo = nil
artem = nil

🔷 После запуска программы мы увидим следующее:

Volvo is being initialized
Artem is being initialized

То есть ни один из деинициализаторов не был вызван, хотя для экземпляров мы установили значение nil.

🔗 Это означает, что у объектов осталось по одной ссылке, и после освобождения эти экземпляры никуда не делись.

Цикл сильных ссылок препятствует освобождению экземпляров, что приводит к утечке памяти в приложении.

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

❤️ В следующих публикациях я расскажу, как это сделать. Ставьте лайк, если хотите, чтобы посты выходили быстрее.

@iosdiving