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-თან ლამბდასკრიპტში, რაც თავიდან აცილებს ციკლურ კავშირს.