Ինչպես է աշխատում ավտոմատ հիշողության կառավարումը (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) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
class Apartment {
let unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
print("Apartment \(unit) is being initialized")
}
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 չի դեինիցիալիզացվում
Խնդրի լուծման համար օգտագործեք թեթև կամ անհայտ հղումներ: Այս դեպքում, tenant-ը Apartment-ում պետք է լինի թեթև հղում:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
class Apartment {
let unit: String
weak var tenant: Person? // Օգտագործում է թեթև հղում
init(unit: String) {
self.unit = unit
print("Apartment \(unit) is being initialized")
}
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 դեինիցիալիզացվում է
Ընտրությունը թեթև կամ անհայտ հղում օգտագործելու միջև՝ կախված է օբյեկտների հարաբերությունից:
- Եփեր (Weak): Եթե մեկը օբյեկտներից կարող է դառնալ
nilմինչ մյուսը դեինիցիալիզացվի (ընտրովի արժեք): - Անհայտ (Unowned): Եթե մեկը օբյեկտներից միշտ կլինի առկա, մինչեւ մյուսը (չընտրովի արժեք):
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) is being deinitialized")
}
}
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 // Օգտագործում է թեթև հղում
guard let self = self else { return "" } // Ստուգում է գոյությունը
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: AnHTMLElement? = AnHTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil // AnHTMLElement դեինիցիալիզացվում է
Օգտագործումը [unowned self] հնարավոր է, եթե վստահ եք, որ օրինակն (self) չի դառնա nil մինչեւ փակագծի կատարման ժամանակ:
ARC-ը զգալիորեն պարզեցրել է հիշողության կառավարմանը Swift-ում՝ համեմատած ձեռքով կառավարմանի հետ, բայց պահանջում է հասկանալ հղումների տեսակները՝ կանխելու համար հիշողության լիքերը։