Циклҳои нигоҳдорӣ (retain cycles) дар iOS чист?
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 را به صورت ضعیف تغییر میدهیم:
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 |
کمی سریعتر |
همچنین، حلقههای نگهداری ممکن است در بستهها (closures) نیز رخ دهند.
وقتی یک بسته، ویژگیهای نمونه یا self را میگیرد، میتواند ارجاع قوی به شیء ایجاد کند. اگر این شیء نیز ارجاع قوی به بسته داشته باشد (مثلاً از طریق ویژگی-بسته یا delegate)، حلقه نگهداری ایجاد میشود.
چگونه مشکل حلقههای نگهداری در بستهها را حل کنیم:
از لیست ضبط (capture list) استفاده کنید.
class SomeClass {
var value = 0
func doSomethingDelayed() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in // لیست ضبط با weak self
guard let self = self else { return } // بررسی `nil` برای weak self
self.value += 1
print("مقدار اکنون \(self.value)")
}
}
}
در این مثال، [weak self] یک ارجاع ضعیف به self در داخل بسته ایجاد میکند، که از حلقه نگهداری جلوگیری میکند.