Память
October 28

Unowned ссылки и неявно развернутые optional свойства

👋 В этой статье рассмотрим эффективность применения неявно развернутых опциональных свойств для создания взаимозависимости в классах без риска возникновения циклических ссылок.

Зачем это нужно?

Представим ситуацию, когда есть модель данных, в которой каждый отдел имеет своего руководителя, а каждый руководитель относится к конкретному отделу. Это прямая и естественная взаимосвязь, но как ее реализовать в Swift, избегая циклических ссылок и обеспечивая корректное управление памятью? Ответ кроется в комбинировании неявно развернутых опциональных свойств с обычными свойствами.

Department и Employee

Рассмотрим два класса, Department и Employee. В нашем случае класс Department будет хранить неявно развернутое опциональное свойство, представляющее руководителя, а класс Employee будет содержать unowned ссылку на Department.

class Department {
    let name: String
    var head: Employee!
    
    init(name: String, headName: String) {
        self.name = name
        self.head = Employee(name: headName, department: self)
    }
}

class Employee {
    let name: String
    unowned let department: Department
    
    init(name: String, department: Department) {
        self.name = name
        self.department = department
    }
}

Как это работает?

  1. Некоторые определения. Класс Department имеет два свойства: неизменяемое name и изменяемое head, которое является неявно развернутым опционалом. Это значит, что по умолчанию оно имеет значение nil, но после инициализации можно без опаски с ним работать, не применяя оператор развертки.
  2. Инициализация. При создании экземпляра Department передаем имя руководителя в инициализатор и тут же создаем экземпляр Employee, связывая его с руководителем отдела.
  3. Устранение циклических ссылок. Класс Employee хранит unowned ссылку на отдел. Это значит, что, даже если Employee ссылается на Department, это не создает циклическую ссылку: если Department будет уничтожен, Employee не будет удерживать ссылку на него, что позволяет избежать утечки памяти.

Пример использования

Теперь, создавая объект класса Department, можно сразу получить информацию о руководителе отдела.

let department = Department(name: "Mobile Development", headName: "Ivanov")
print("The head of the \(department.name) department is \(department.head.name).")

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

👍 Ставьте лайк, если хотите, чтобы подобные посты выходили чаще.