Sobes.tech
Middle

Ինչպես է աշխատում ավտոմատ հիշողության կառավարումը (ARC) Swift-ում?

sobes.tech AI

Պատասխան AI-ից

ARC (Ավտոմատ Հղումային Հաշվարկ)-ը Swift-ում հետևում և կառավարում է ձեր հավելվածների հիշողության օգտագործումը: Այն ինքնաբերաբար ազատում է դասի օրինակների օգտագործած հիշողությունը, երբ նրանք այլևս անհրաժեշտ չեն, ինչը կանխում է հիշողության լիքերը:

Նոր դասի օրինակ ստեղծելիս, ARC-ը հատկացնում է հիշողության բլոկ, որը պահպանում է այդ օրինակին վերաբերող տեղեկությունները: Երբ օրինակն այլևս անհրաժեշտ չէ, ARC-ը ազատում է այդ հիշողությունը՝ այն օգտագործելու համար այլ նպատակներով:

ARC-ը աշխատում է հաշվել յուրաքանչյուր դասի օրինակին վերաբերող ուժեղ հղումների (strong references) քանակը: Ուժեղ հղում՝ դա հղում է, որը պահպանում է օրինակին հիշողության մեջ: Երբ ուժեղ հղումների քանակը դառնում է զրո, ARC-ը ազատում է այդ հիշողությունը:

Swift-ում կան երեք տեսակի հղումներ, որոնք ազդում են ARC-ի վրա:

  1. Ուժեղ հղումներ (Strong References): ավելացնում են հղումների հաշվիչը օրինակին:
  2. Եփեր հղումներ (Weak References): չեն ավելացնում հղումների հաշվիչը: օգտագործվում են ուժեղ հղումների ցիկլերը խուսափելու համար, երբ երկու դասի օրինակներ պահպանում են միմյանց ուժեղ հղումներ: Եփեր հղումը ավտոմատ կերպով դառնում է nil՝ երբ օբյեկտը դեինիցիալիզացվում է:
  3. Անհայտ հղումներ (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 չի դեինիցիալիզացվում

Խնդրի լուծման համար օգտագործեք թեթև կամ անհայտ հղումներ: Այս դեպքում, tenantApartment-ում պետք է լինի թեթև հղում:

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-ում՝ համեմատած ձեռքով կառավարմանի հետ, բայց պահանջում է հասկանալ հղումների տեսակները՝ կանխելու համար հիշողության լիքերը։