Видеокурсы по изучению языка программирования Swift. Подробнее

Циклы сильных ссылок между экземплярами классов

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

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

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

Вы сами решаете, когда сделать вместо сильной (strong) ссылки слабую (weak) или бесхозную (unowned). Подробнее об этом можно прочитать в разделе Замена циклов сильных ссылок между экземплярами классов. Однако перед тем как узнать, в каких случаях разрешить сильный ссылочный цикл, давайте узнаем что вызывает его.

Ниже приведен пример того, как сильный ссылочный цикл может быть создан по ошибке. В этом примере мы определяем два класса Person и Apartment, которые создают модель блока квартир с их жителями:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) освобождается") }
}
 
class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    var tenant: Person?
    deinit { print("Апартаменты \(unit) освобождаются") }
}

Каждый экземпляр Person имеет свойство name типа String и опциональное свойство apartment, которое изначально nil. Свойство apartment опционально, так как наша персона не обязательно всегда должна иметь апартаменты.

Аналогично, что каждый экземпляр Apartment имеет свойство unit типа String и опциональное свойство tenant, которое изначально nil. Свойство tenant опциональное, потому как не всегда в апартаментах кто-то живет.

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

Следующий фрагмент кода определяет две опциональные переменные с именами john и unit4A, которые будут назначены определенным экземплярам классов Apartment и Person. Оба значения переменных равны nil, в силу того, что они опциональны:

var john: Person?
var unit4A: Apartment?

Теперь вы можете создать свои экземпляры Person и Apartment и присвоить их этим переменным john, unit4A:

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

Вот как выглядят сильные связи после того создания и присваивания этих двух экземпляров. Переменная john имеет сильную связь с экземпляром класса Person, переменная unit4A имеет сильную связь с экземпляром Apartment:

Теперь вы можете соединить эти два экземпляра вместе, так что житель будет иметь апартаменты, а апартаменты будут иметь своих жителей. Обратите внимание, что восклицательный знак (!) используется для развертывания и допуска к экземплярам, хранимым в опциональных переменных john, unit4A, так что установить значения свойством можно в такой форме:

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

Вот как выглядят сильные связи после того, как мы соединили экземпляры:

К сожалению, соединяя таким образом, образуется цикл сильных ссылок между экземплярами. Экземпляр Person имеет сильную ссылку на экземпляр Apartment, экземпляр Apartment имеет сильную ссылку на экземпляр Person. Таким образом, когда вы разрушаете сильные ссылки, принадлежащие переменным john и unit4A, их количество все равно не падает до нуля, и экземпляры не освобождаются:

john = nil
unit4A = nil

Обратите внимание, что ни один деинициализатор не был вызван, когда вы присваивали nil. Цикл сильных ссылок предотвратил экземпляры Person и Apartment от освобождения, что вызывает утечку памяти в вашем приложении.

Вот как выглядят сильные ссылки после того, как вы присвоили nil переменным, john, unit4A:

Сильные взаимные ссылки остались между экземплярами Person и Apartment и не могут быть разрушены.

 

Swift: 
4.0