Как работи механизмът за автоматично управление на паметта (ARC) в езика Swift?
sobes.tech AI
Отговор от AI
ARC (Автоматично управление на препратки) автоматично следи и управлява препратките към екземпляри на класове. Той освобождава паметта, заета от екземпляра, когато към него няма повече силни препратки. 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) се освобождава")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
}
deinit { // извиква се при освобождаване
print("Апартамент \(unit) се освобождава")
}
}
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 // Нищо не се отпечатва, защото има цикъл от силни препратки
Решаване на цикъл от силни препратки с weak:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit {
print("\(name) се освобождава")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Слаба препратка към Person
init(unit: String) {
self.unit = unit
}
deinit {
print("Апартамент \(unit) се освобождава")
}
}
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) се освобождава")
}
}
class CreditCard {
let number: UInt64
unowned let customer: Customer // Безпритежателна препратка към Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit {
print("Карта #\(number) се освобождава")
}
}
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) се освобождава")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "здравей, свят")
print(paragraph!.asHTML())
paragraph = nil // HTMLElement се освобождава