Программирование
ARC в Swift: лучшие практики для предотвращения утечек памяти
При использовании ссылочных типов в вашем проекте вы можете столкнуться с тихими ошибками или неожиданным поведением из-за автоматического подсчета ссылок (ARC).
Что такое ARC?
Автоматический подсчет ссылок (Automatic Reference Counting, ARC) в Swift — это функция управления памятью, которая обеспечивает эффективное выделение и удаление ресурсов. Она функционирует аналогично сборщику мусора, но с большей точностью, адаптированной к компилятору Swift. ARC автоматически отслеживает и управляет количеством ссылок на объекты, освобождая память путем деаллокации объектов, которые больше не используются. Это помогает оптимизировать производительность приложений и предотвратить утечки памяти, что делает ее одним из важнейших аспектов разработки на Swift.
Как работает ARC
Когда вы создаете ссылку на объект в Swift, автоматический подсчет ссылок (ARC) инициализирует счетчик с начальным значением 1. Каждый раз, когда ссылка копируется, ARC увеличивает счетчик на 1, чтобы отследить дополнительную ссылку. И наоборот, когда ссылка используется в последний раз, ARC уменьшает счетчик на 1. Этот процесс увеличения и уменьшения количества ссылок обрабатывается операторами retain и release. Как только счетчик достигает 0, ARC автоматически деаллокирует объект из памяти, обеспечивая эффективное управление памятью и предотвращая возможные утечки памяти.
Давайте разберемся в этом на примере:
class Weight {
var weight = 0
}
func test() {
// once you created the reference variable retain operator comes in action and create reference counter for object for weight and set it to 1
let reference1 = Weight() // reference count = 1
// let's create one more reference for Weight object by coping reference1
let reference2 = reference1 // reference count = 2
// This is the last time when we used reference1 so now release operator will decrease reference count by 1
// reference count = 1
reference2.weight = 56
print("Weight = ",reference2.weight)
// This is the last time when we used reference2 so now release operator will decrease reference count by 1
// reference count = 0
// Once reference count become 0 ARC will remove Weight object from memory
print("end----------")
}
Проблемы, с которыми вы можете столкнуться при неправильном подходе
При использовании ссылочных типов в вашем проекте, особенно когда вы полагаетесь на наблюдаемое свойство объекта, вы можете столкнуться с тихими ошибками или неожиданным поведением из-за автоматического подсчета ссылок (ARC). Чтобы минимизировать эти риски, рекомендуется придерживаться типов значений, таких как структуры и перечисления, которые помогут предотвратить проблемы во время выполнения. Кроме того, будущие оптимизации ARC могут потенциально изменить поток вашего проекта, а это значит, что, хотя ваш код может работать нормально сегодня, его поведение может измениться по мере развития ARC. Более того, если ваш код создает сильный цикл ссылок, это может привести к утечкам памяти, что еще больше ухудшит производительность и стабильность приложения.
Что такое сильный цикл ссылок
Сильный цикл ссылок в Swift возникает, когда два или более объекта держат сильные ссылки друг на друга, не позволяя ARC уменьшить количество ссылок до нуля. В результате эти объекты остаются в памяти, что приводит к утечке памяти, поскольку ARC не может их деаллоцировать.
xxxxxxxxxx
class Person {
var name: String
var vehicle: Vehicle?
init(_ name: String) {
self.name = name
print("Person object is created")
}
deinit {
print("Name of the person is \(name)")
}
}
class Vehicle {
var owner: Person?
var modal: String
init(_ modal: String) {
self.modal = modal
print("Vehicle object is created")
}
deinit {
print("Model of vehicle is \(modal)")
}
}
func test() {
let person = Person("John") // Person reference count = 1
let vehicle = Vehicle("ModelX") // Vehicle reference count = 1
person.vehicle = vehicle // Vehicle reference count = 2
vehicle.owner = person // Person reference count = 2
// we are using person and vehicle for last time hear so
// Person reference count = 1
// Vehicle reference count = 1
// This will create strong reference cycle
}
Выход тестовой функции:
Способы борьбы с сильным циклом ссылок
Вы можете разорвать сильный цикл ссылок в Swift, используя weak и unowned ссылки, поскольку они не участвуют в подсчете. Это означает, что они не увеличивают количество ссылок на объекты, на которые они указывают. В результате, когда на объект есть только слабые или бесхозные ссылки, ARC может деаллоцировать его память, эффективно разрывая цикл.
xxxxxxxxxx
class Person {
var name: String
weak var vehicle: Vehicle?
init(_ name: String) {
self.name = name
print("Person object is created")
}
deinit {
print("Name of the person is \(name)")
}
}
class Vehicle {
weak var owner: Person?
var modal: String
init(_ modal: String) {
self.modal = modal
print("Vehicle object is created")
}
deinit {
print("Model of \(owner!.name) vehicle is \(modal)") // this can give you an crash
}
}
func test() {
let person = Person("John")
let vehicle = Vehicle("ModelX")
person.vehicle = vehicle
vehicle.owner = person
}
При использовании weak
ссылки в Swift, если память ссылающегося объекта деаллоцируется, слабая ссылка автоматически становится nil
. Хотя вы можете использовать опциональную цепочку для безопасной обработки этой ситуации, ее использование может привести к ошибкам клиента. Эти проблемы сложно обнаружить и отладить, поскольку они часто связаны с неожиданным появлением значений nil
во время выполнения. Отладка таких проблем может занять много времени и быть сложной, поэтому важно тщательно подходить к использованию слабых ссылок в коде.
Чтобы предотвратить проблемы, возникающие из-за неожиданного превращения weak
ссылки в nil
, в Swift можно использовать функцию withExtendedLifetime
. Эта функция продлевает время жизни объекта, на который ссылаются, на время действия области видимости, в которой он используется, гарантируя, что слабый объект останется в памяти на протяжении всего выполнения функции.
xxxxxxxxxx
func test() {
let person = Person("John")
let vehicle = Vehicle("ModelX")
person.vehicle = vehicle
vehicle.owner = person
withExtendedLifetime(person) {} // This will hold the wear reference
}
Хотя withExtendedLifetime
может помочь решить проблемы, связанные со слабыми ссылками, продлевая время жизни объекта в функции, он имеет свои собственные сложности. Использование этого подхода может увеличить сложность вашей кодовой базы и требует тщательного рассмотрения того, как будет вести себя ваш код. Неправильное использование может привести к непредвиденным побочным эффектам, что усложнит сопровождение и отладку проекта.
Лучшая практика
Чтобы избежать утечек памяти, проблем во время выполнения и сложностей со слабыми ссылками в Swift, важно предотвратить создание циклов сильных ссылок. Один из эффективных подходов — уделить время продуманному структурированию кодовой базы. Разделив обязанности классов и убедившись, что каждый класс имеет четкую и изолированную цель, вы сможете свести к минимуму вероятность непреднамеренного создания сильных циклов ссылок. Такая практика не только помогает поддерживать чистоту кодовой базы, но и снижает риск столкнуться со сложными проблемами управления памятью в будущем.
xxxxxxxxxx
class Person {
var name: String
init(_ name: String) {
self.name = name
}
}
class User {
var id: Int
var personalInfo: Person?
init(id: Int, personalInfo: Person?) {
self.id = id
self.personalInfo = personalInfo
}
func printInfo() {
print("ID of \(personalInfo!.name) is \(id)")
}
}
class Vehicle {
var personalInfo: Person?
var modal: String
init(personalInfo: Person?, modal: String) {
self.personalInfo = personalInfo
self.modal = modal
}
func printInfo() {
print("Model of \(personalInfo!.name) vehicle is \(modal)")
}
}
func test() {
let person = Person("John")
let vehicle = Vehicle(personalInfo: person, modal: "ModelX")
let user = User(id: 007, personalInfo: person)
vehicle.printInfo()
user.printInfo()
}
Здесь видно, что мы создали один отдельный класс, чтобы предотвратить цикл сильных ссылок. Приложив немного усилий к этому процессу, вы можете быть уверены, что в будущем не столкнетесь с какими-либо случайными проблемами во время выполнения.
Используйте [weak self]
При использовании замыканий в Swift важно помнить о потенциальных циклах сильных ссылок, особенно если замыкание захватывает self
. Чтобы предотвратить такие циклы, следует использовать [weak self]
в списке захвата замыкания. Это гарантирует, что ссылка на self
будет слабой, что позволит ARC деаллоцировать его, если нет других сильных ссылок, и тем самым избежать утечек памяти.
-
Новости4 недели назад
Видео и подкасты о мобильной разработке 2025.11
-
Новости7 дней назад
Видео и подкасты о мобильной разработке 2025.14
-
Видео и подкасты для разработчиков3 недели назад
Javascript для бэкенда – отличная идея: Node.js, NPM, Typescript
-
Новости3 недели назад
Видео и подкасты о мобильной разработке 2025.12