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) ინიცირებულია")
    }

    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 // პიროვნება არ დეინიციალიზდება
unit4A = nil // კორპუსი არ დეინიციალიზდება

მოსახერხებელია, გამოიყენოთ სუსტი ან შეუკვეთელი მითითებები. ამ შემთხვევაში, 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 // პიროვნება დეინიციალიზდება
unit4A = nil // კორპუსი დეინიციალიზდება

შესაძლებელია, აირჩიოთ სუსტი ან შეუკვეთელი მითითება დამოკიდებულია ობიექტების ურთიერთობაზე:

  • სუსტი (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) დეინიციალიზებულია")
    }
}

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) დეინიციალიზებულია")
    }
}

var paragraph: AnHTMLElement? = AnHTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())

paragraph = nil // AnHTMLElement დეინიციალიზდება

გამოყენება [unowned self] შესაძლებელია, თუ დარწმუნებული ხართ, რომ მაგალითი (self) არ გახდება nil სანამ დახურვა შესრულდება.

ARC მნიშვნელოვნად გამარტივებს მეხსიერების მართვას Swift-ში, შედარებით ხელით მართვასთან, მაგრამ მოითხოვს შელესვის ტიპების გაგებას, რათა თავიდან აიცილოთ მეხსიერების ნაკლებობა.