Reference types

date
Jun 8, 2023
slug
reference-types
status
Published
tags
memory
summary
type
Page
В Swift, память управляется автоматически с помощью механизма ARC (Automatic Reference Counting), который отслеживает количество ссылок на объекты и освобождает память, когда на объект престают ссылаться.
 
🌱
Если вы только изучаете Swift, и еще не понимате все аспекты его работы, не расстраивайтесь - это нормально, через это прошел каждый крутой разработчик. Тем не менее есть вещи, которые лучше крепко запомнить уже сейчас. Спустя время эта информация будет лаконично использована вашим мозгом именно в тот момент когда это понадобится:
ARC работает только с экземплярами классов. Структуры и перечисления являются типами значений, а не ссылочными типами. Они не хранятся и не передают свои значения по ссылке
 
Когда вы создаете объект класса, память выделяется для хранения информации об этом объекте. Эта информация включает тип объекта, его значение и связанные с ним свойства.
Когда объект больше не нужен, память, занимаемая им, освобождается и используется для других целей. Таким образом, неиспользуемые объекты не занимают память. Однако, если память объекта освобождается, нельзя получить доступ к его свойствам или методам. Если вы попытаетесь обратиться к такому объекту, ваше приложение может выдать ошибку и прекратить работу.
Чтобы объект не исчезал, система учитывает количество свойств, констант и переменных, которые ссылаются на этот объект. Если хотя бы одна ссылка на объект существует, он не будет удален.
При присваивании объекта свойству, константе или переменной создается "сильная ссылка" на этот объект. Наличие "сильной ссылки" не позволяет ARC быть удаленным.
 

Сильные ссылки

При создании объекта класса, он имеет некоторый набор данных, обеспечивающих его хранение в памяти, а другим объектам обращаться к нему.
Среди этих данных содержится информация о количестве сильных ссылок на объект. Обычное присвоение обекта клааса к константе или переменной увеличивает его значение на единицу. Таким образом ARC в каждый момент времени понимает сколько ссылок существует на этот объект.
Эта информация существует с момента создания объекта, хранится вместе со всеми метаданными. Следовательно сразу после инициализации объекта в переменную его значение равно единице.
Если счетчик сильных ссылок на объект равен нулю, то ARC автоматически освобождает память, выделенную для объекта.
Как если бы это был ваш дом и только вы знаете о его существовании (может небольшая дача в лесу(?)))). Вы единственный его обитатель - счетчик сильных ссылок “на этот дом” будет равен 1
 
notion image
Если у вас есть семья и друзья которые приходят к вам на вечеринки, количество ссылок будет равно количеству всех людей, кто знает и пользуется домом.
 
notion image
 
Если вы продали дом и вместе с семьей переехали в город, предупредили всех друзей, что вечеринки переносятся в другое место, то ARC может без опасений снести ваш старый дом и на его месте построить все что угодно.
т.е. освободившаяся память будет переиспользована для хранения новых объектов.
📌
Обратите внимание, что если переехали только вы с друзями, а жена осталась в старом доме, то строительная кампания не сможет снести дом.
Возвращаясь из метафор к Swift - пока существует хотя бы одна сильная ссылка на объект, он не может быть деинициализирован.
notion image
 

Слабые ссылки

Используются для создания некоторой формы ссылки на объект, не увеличивая при этом его счетчик сильных ссылок, следовательно наличие любого количества слабых ссылок не препятствует освобождению памяти занимаемой объектом.
Объект по слабой ссылке является опциональным значением, поэтому его наличие не гарантируется. Именно по этой причине, захваченный в замыкании [weak self] обычно раскрывается через guard let self else.
 
🤌
Слабая ссылка может быть установлена только для классов и для определения типа в протоколе. Тогда как структуры и перечисления не могут иметь слабых ссылок.
 
Счетчик слабых ссылок хранится в отдельной структуре - SideTable. Таким образом доступ к объекту по сильной ссылке быстрее чем по слабой, ведь программе не требуется выполнять дополнительное обращение за ссылкой.
SideTable не существует при инициализации объекта, но создается в двух случаях:
  • В момент создания первой weak ссылки;
  • При переполнении раздела памяти, выделенного для хранения счетчика strong и unowned ссылок.
 
Возвращаясь к нашему примеру с домом. Можно сказать, что слабые ссылки это ваши соседи, которые просто знают о его существовании, но приходят к вам только в большие праздники. И то, вы всегда сопровождаете их - вы “держите” сильную сильную ссылку на дом, что бы он существовал, пока они могут воспользоваться уборной.
 
notion image
 
Наличие таких соседей не спасет дом от сноса, в случае вашего переезда, но например если вы все еще явлетесь его хозяином и уехали в отпуск, они могут покормить вашего кота.
Обратите внимание, что если вы (и все остальные объекты) прекратите “удерживать ссылку на дом”, пока соседи кормят кота, то процесс кормления кота прервется вместе с удалением всего дома. Это одна из возможных проблем при работе в многопоточной среде, неважно, используете ли вы GCD, или Swift Concurrensy.
 
🤌
В этот момент у вас должен возникнуть вопрос - зачем нужны weak ссылки, если с ними существуют проблемы.
Ответ ниже, в разделе Цикл сильных ссылок. Но рекомендую не торопиться и ознакомиться с последним типом ссылок 👇
 

Безхозные ссылки

Это еще один вид ссылок, доступных в Swift, которые используются для предотвращения циклов сильных ссылок. Они, как и слабые ссылки, не увеличивают счетчик ссылок на объект.
Однако, в отличие от слабых ссылок, unowned ссылки считаются "ненулевыми", то есть они не могут быть nil. Но unowned не гарантирует наличие объекта по ссылке.
 
Если вы используете unowned ссылку на объект, который уже был освобожден, это приведет к падению приложения.
 

Рекомендации к использованию

  • unowned ссылки можно использовать в тех случаях, когда объект, на который указывает ссылка, является неизменяемым (immutable), и вы уверены, что он не будет освобожден до тех пор, пока ссылка на него существует.
    •  
      🤌
      Тем не менее вы должны осозновать риски того, что однажды написанный вами код, в будущем может быть изменен другим разработчиком и если ваша реализация трудно читаема или квалификация другого разработчика будет недостаточна, что бы понять архитектуру вашего решения, это может привести к проблемам.
 
  • Наличие сильной логической связи между объектами, которая позволяет однозначно связать два объекта. При такой связи можно однозначно подтвердить утверждение: “Объект A может существовать без B, но B не может без A”.
    • В этом случае B может иметь unowned ссылку на A. Из ярких примеров можно придумать такие кейсы:
    • Клиент банка и его банковский счет;
    • Спортсмен и его результат в виде спорта;
    • Родитель и его ребенок итд.
    •  
      Логическая составляющая таких связок потенциально может обеспечить косвенную безопасность доступа по unowned ссылке при работе в команде.
       
Код к примеру
 
В этом примере моделируется спортивное достижение тежелоатлета.
 
notion image
 
 
Использование unowned ссылок может привести к краху программы, если объект, на который ссылается ссылка, уже был освобожден. Однако, есть определенные ситуации, когда использование unowned ссылок может быть предпочтительнее, чем использование слабых ссылок:
  • unowned ссылки имеют лучшую производительность, так как при обращении к такому объекту не нужно ходить в side table и проверять связи.
 

Цикл сильных ссылок

 
 

Полезные материалы


© Evgenii Chernyshov 2023