Как работи автоматичното управление на паметта (ARC) в Swift?
sobes.tech AI
Отговор от AI
ARC (Автоматично преброяване на референциите) в Swift следи и управлява използването на паметта на вашите приложения. Той автоматично освобождава паметта, използвана от екземплярите на класове, когато вече не са необходими, което предотвратява течове на памет.
При създаване на нов екземпляр на клас, ARC заделя блок памет за съхранение на информация за този екземпляр. Когато екземплярът вече не е нужен, ARC освобождава тази памет, за да може да бъде използвана за други цели.
ARC работи, като брои броя на силните препратки (strong references) към всеки екземпляр на клас. Силната препратка е препратка, която държи екземпляра в паметта. Когато броят на силните препратки към екземпляра стане нула, ARC освобождава свързаната с него памет.
В Swift съществуват три типа препратки, които влияят на ARC:
- Силни препратки (Strong References): увеличават броя на препратките към екземпляра.
- Слаби препратки (Weak References): не увеличават броя на препратките. Използват се, за да се избегнат цикли на силни препратки, когато два екземпляра на клас държат силни препратки един към друг. Слабата препратка автоматично става
nilпри деинициализация на обекта, към който сочи. - Несвързани препратки (Unowned References): също не увеличават броя на препратките. Използват се, когато сте сигурни, че препратеният екземпляр винаги ще съществува, докато съществува текущият екземпляр. Не могат да бъдат опционални (
nil). Опитът за достъп до деинициализиран обект чрез несвързана препратка ще доведе до грешка по време на изпълнение.
Пример за цикъл на силни препратки:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) се инициализира")
}
deinit {
print("\(name) се деинициализира")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
print("Апартамент \(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 не се деинициализира
За решаване на цикъла, използвайте слаби или несвързани препратки. В този случай, tenant в Apartment трябва да бъде слаба препратка:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) се инициализира")
}
deinit {
print("\(name) се деинициализира")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Използване на слаба препратка
init(unit: String) {
self.unit = unit
print("Апартамент \(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 се деинициализира
Изборът между слаба и несвързана препратка зависи от връзката между обектите:
- Слаба: Ако един от обектите може да стане
nilпреди другият да бъде деинициализиран (опционална стойност). - Несвързана: Ако един от обектите винаги съществува, докато съществува другият (неопционална стойност).
ARC също управлява паметта, използвана от затваряния (closures), които захващат екземпляри на класове. Цикли на силни препратки могат да възникнат, когато затваряне захване екземпляр на клас, а този екземпляр също има силна препратка към затварянето. За да се избегне това, се използват списъци за захващане (capture lists) в затварянията.
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
// Без списък за захващане, възможен силен цикъл
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: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // HTMLElement не се деинициализира
Решение с използване на списък за захващане [weak self]:
class AnHTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { [weak self] in // Използване на weak self
guard let self = self else { return "" } // Проверка за съществуване на 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: AnHTMLElement? = AnHTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // AnHTMLElement се деинициализира
Използването на [unowned self] е възможно, ако сте сигурни, че екземплярът на класа (self) няма да стане nil преди изпълнението на затварянето.
ARC значително улеснява управлението на паметта в Swift в сравнение с ръчното управление, но изисква разбиране на типовете препратки, за да се избегнат течове на памет.