Как устроен механизм автоматического управления памятью (ARC) в языке Swift?
Sobes Copilot
Получайте ответы в реальном времени
Ответ от нейросети
sobes.tech AI
ARC (Automatic Reference Counting) автоматически отслеживает и управляет ссылками на экземпляры классов. Он освобождает память, занимаемую экземпляром, когда на него больше нет сильных ссылок. ARC работает только с экземплярами классов, не со структурами или перечислениями, так как они являются типами-значениями.
Три типа связей между экземплярами:
- Strong (Сильная): Связь, которая увеличивает счетчик ссылок на экземпляр. По умолчанию все ссылки являются сильными.
- Weak (Слабая): Связь, которая не увеличивает счетчик ссылок. Используется для предотвращения циклов сильных ссылок. Ссылки типа
weakвсегда опциональны, так как экземпляр может быть освобожден в любой момент. - Unowned (Бесхозная): Связь, которая не увеличивает счетчик ссылок и используется, когда гарантировано, что связанный экземпляр будет существовать дольше текущего. Необязательно, но при попытке доступа к освобожденному экземпляру вызывает ошибку выполнения.
Циклы сильных ссылок возникают, когда два или более экземпляра класса имеют сильные ссылки друг на друга, предотвращая их освобождение ARC. Для разрешения циклов используются weak или unowned ссылки.
Пример цикла сильных ссылок:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit { // вызывается при освобождении
print("\(name) is being deinitialized")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
}
deinit { // вызывается при освобождении
print("Apartment \(unit) is being deinitialized")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil
unit4A = nil // Ничего не на1печатается, так как произошел цикл сильных ссылок
Разрешение цикла сильных ссылок с использованием weak:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Слабая ссылка на Person
init(unit: String) {
self.unit = unit
}
deinit {
print("Apartment \(unit) is being deinitialized")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil // Освобождает Person
unit4A = nil // Освобождает Apartment
Разрешение цикла сильных ссылок с использованием unowned:
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class CreditCard {
let number: UInt64
unowned let customer: Customer // Бесхозная ссылка на Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit {
print("Card #\(number) is being deinitialized")
}
}
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
john = nil // Освобождает Customer и CreditCard
ARC в замыканиях: Замыкания также могут создавать сильные циклы ссылок, захватывая экземпляры классов. Для предотвращения этого используются списки захвата ([weak self] или [unowned self]).
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { // lazy для возможности использовать self до инициализации
[unowned self] in // Список захвата с "unowned self"
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // Освобождает HTMLElement