Swift: Automatic Reference Counting (Автоматический подсчет ссылок)

Работает только с reference типам, что логично.
Каждый раз как мы создаем экземпляр класса, ARC выделяет кусок памяти для хранения информации об этом экземпляре (тип экземпляра + значения всех хранимых свойств)
Когда ссылок на этот экземпляр не станет — ARC автоматически освободит занимаемую этим экземпляром память. Работает это за счет подсчета ссылок на экземпляр. Каждое свойство, или переменная имеющие сильную связь (strong reference) к экземпляру увеличивает счетчик ссылок на 1.

На выходе мы получаем

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

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

Мы бы хотели увидеть здесь сообщения о deinit для этих обьектов, но увидим лишь

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

Чтобы избежать такой ситуации ввели week reference (слабые ссылки) и unowned reference (ссылка без обладания). Разница в том, что weak — на протяжении работы программы вполне может стать nil, в то время как unowned reference обязана иметь значение после инициализации

Ради интереса проверим как поведет наш код если мы сделаем weak сначала свойство b в классе ClassA

В консоли мы увидим следующее

Как видим (1) код a = nil не привел к вызову деинициализатора, т.к. на обьект типа ClassA осталась сильная ссылка из обьекта ClassB
Но как только мы присвоили nil переменной b — это вызывало сначала уничтожение самого обьекта типа ClassB (2)
deinit B
После этого пропала сильная ссылка свойства var a, которая являлась последней оставшейся, и a — тоже вызвал код деинициализации (3)

А теперь сделаем как было var b :ClassB?, но в ClassB поменяем на слабую ссылку

Вывод в консоли изменился

После присвоения nil переменной a на нее не стало сильных ссылок (в ClassB она теперь слабая), и тут же вызвался деиницианализатор (1), и после этого разрушилась сильная ссылка на b. Поэтому после присвоения nilb, он тоже без проблем вызвал свой деиницианализатор (2)
В этом случае оба типа позволяют свойству быть nil

В случае unowned reference — мы обязаны инициализировать свойство помеченное как unowned

В этом случае только ClassC позволено иметь свойство d как nil, но при инициализации ClassD мы обязаны иметь инициализированный ClassC
Но как выстроить инициализацию двух классов, если они оба при создании должны иметь созданные обьекты друг друга. Прийдется комбинировать Unowned References с Implicitly Unwrapped Optional свойством.

Чтобы создать столицу, мы должны иметь созданную страну, но страна без страны тоже не должна создаваться. Поэтому City имеет unowned ссылка на Country, и инициализирует это свойство в init по переданному параметру
А при создании Country мы в параметрах передаем нужны данные как для создания Country (name), так и City(capitalName). И в инициализаторе

На данный момент этот код хоть и является референсным из книги «The Swift Programming Language» из раздела «Unowned References and Implicitly Unwrapped Optional Properties«, но не компилируется и выдает ошибку на попытке присвоить значение self.capitalCity
Связано это видимо с изменениями в 1.2, так что чтобы этот кода заработал нужно изменить с let на var capitalCity: City!
В мануале для swift 2.0 кстати исправили таки let на var

Strong Reference Cycles for Closures — циклические сильные ссылки для замыканий

на выходе будет

Как видим, после того как мы обратились к borderedText — замыкание было создано, и для его работы понадобилась ссылка на self, чтоб была возможность обратиться к text. По умолчанию ссылка — сильная, вот и возник цикл, diagnose2 имеет сильную ссылку на замыкание, а в замыкании — сильная ссылка на diagnose2.
Чтобы решить эту проблему, ввели capture list (список захвата) как часть определения замыкания
В общем виде замыкание со списком захвата выглядит так (в квадратных скобках перед началом параметров)

Если у замыкания нет параметров — этот список следует поместить в самое начало тела замыкания

Правила те же, если параметр никогда не должен быть Nil — используем unowned reference, иначе — weak

Чтобы решить проблему в классе Diagnose, поменяем свойство на следующее

Теперь вывод будет заканчиваться как мы и ожидали:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *