Sobes.tech
Middle

iOS-ში retenციული ციკლები (retain cycles) რა არის?

sobes.tech AI

პასუხი AI-სგან

რეგულარული ციკლები წარმოიქმნება, როდესაც ორი ან მეტი ობიექტი ძლიერ კავშირშია ერთმანეთთან, ქმნის "დაკეტილ წრეს". შედეგად, თითოეული ობიექტის მითითების გამოთვლილი არასდროს აღწევს ნულს და ის ვერ ხერხდება ARC-ის (ავტომატური მითითების გამოთვლის) მიერ. ეს იწვევს მეხსიერების გაჟონვას, რადგან ობიექტები რჩება მეხსიერებაში მაშინაც კი, როდესაც ისინი აღარ გამოიყენება.

მაგალითი:

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

    deinit {
        print("Apartment \(unit) დეინიცირებულია")
    }
}

ამ მაგალითში, თუ Person აქვს ძლიერი კავშირი Apartment-თან, ხოლო Apartment-ს აქვს ძლიერი კავშირი Person-თან, მაშინ როდესაც ამ ობიექტების განთავისუფლება სცადეთ, ისინი არ მოიხსნება, რადგან მათი მითითების გამოთვლილი არასდროს აღწევს ნულს.

როგორ დავძლიოთ ციკლური კავშირები:

გამოიყენეთ სუსტი (weak) ან უცხო (unowned) კავშირები.

  • weak: გამოიყენება, როდესაც შესაძლებელია, რომ დაკავშირებული ობიექტი გახდეს nil სიცოცხლის განმავლობაში. კავშირი ავტომატურად ხდება nil, როდესაც ის ობიექტი, რომელსაც ის უკავშირდება, განთავისუფლდება. არის ოპციონალური.
  • unowned: გამოიყენება, როდესაც ცნობილია, რომ დაკავშირებული ობიექტი აქვს იგივე ან უფრო გრძელი სიცოცხლის ხანგრძლივობა, ვიდრე ობიექტი, რომელსაც ის უკავშირდება. არ შეიძლება იყოს nil.

შეცვლილი მაგალითი weak-ის გამოყენებით:

შეცვალეთ tenant კავშირი Apartment კლასში სუსტი (weak):

class Apartment {
    let unit: String
    weak var tenant: Person? // გამოვიყენეთ weak

    init(unit: String) {
        self.unit = unit
        print("Apartment \(unit) ინიცირებულია")
    }

    deinit {
        print("Apartment \(unit) დეინიცირებულია")
    }
}

ახლა, როდესაც Person ობიექტი განთავისუფლდება, tenant კავშირი Apartment-ში ავტომატურად ხდება nil, რაც არღვევს ციკლურ კავშირს და საშუალებას აძლევს Apartment-ს განთავისუფლება.

შედარების სია weak და unowned-ის შორის:

მახასიათებელი weak unowned
კავშირი ტიპი ოპციონალური ოპციონალური არაა
შეიძლება იყოს nil? დიახ, როდესაც ობიექტი განთავისუფლდება არა
როდის გამოიყენო როდესაც ობიექტი შეიძლება გახდეს nil როდესაც ობიექტი არ შეიძლება იყოს nil
შესრულება ცოტათი ნელი nil-ის შემოწმების გამო ცოტათი სწრაფი

ციკლური კავშირები შეიძლება წარმოიქმნას ასევე ლამბდასკრიპტებში.

როდესაც ლამბდასკრიპტი იჭერს instance-ის თვისებებს ან self, ის შეიძლება შექმნას ძლიერი კავშირი ობიექტთან. თუ ეს ობიექტი ასევე აქვს ძლიერი კავშირი ლამბდასკრიპტთან (მაგალითად, თვისების ან დელეგატის საშუალებით), წარმოიქმნება ციკლური კავშირი.

როგორ დავძლიოთ ციკლური კავშირები ლამბდასკრიპტებში:

გამოიყენეთ კაპჩურის სია (capture list).

class SomeClass {
    var value = 0

    func doSomethingDelayed() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in // კაპჩურის სია with weak self
            guard let self = self else { return } // `self`-ის ნულოვანი შემოწმება
            self.value += 1
            print("Value is now \(self.value)")
        }
    }
}

ამ მაგალითში [weak self] ქმნის სუსტი კავშირს self-თან ლამბდასკრიპტში, რაც თავიდან აცილებს ციკლურ კავშირს.