UIKit 4: Xotira boshqaruvi va ARC — Retain Cycle, CoW va Instruments
📱 UIKit va SwiftUI — chuqur
UIKit 4: Xotira boshqaruvi va ARC — Retain Cycle, CoW va Instruments
Holat: 2026-yil sentabr. iOS ilovalarda xotira sizib chiqishi (memory leak) nafaqat ilova tezligini pasaytiradi, balki tizim tomonidan ilovaning majburiy o'ldirilishiga (OOM — Out of Memory Crash) olib keladi. Ushbu darsda ARC qoidalari, real simulyator testlarida o'lchangan 4 ta asosiy Retain Cycle holati, Copy-on-Write (CoW) arxitekturasi va autoreleasepool qo'llanilishi amaliy misollarda ko'rsatiladi.
Mundarija
- ARC mexanizmi: Strong, Weak va Unowned
- [O'lchangan faktlar: Klojurlar va [weak self]](#closures)
- Delegatlar: strong delegate xatosi
- Timer va NotificationCenter xotira tuzoqlari
- Copy-on-Write (CoW) qanday ishlaydi?
- Autoreleasepool: xotira cho'qqilarini (spikes) yo'qotish
- Xotira oqishini topish: Memory Graph va Instruments
- Xulosa
- Manbalar
1. ARC mexanizmi: Strong, Weak va Unowned {#arc-basics}
Swift'da xotira boshqaruvi ARC (Automatic Reference Counting) tomonidan amalga oshiriladi: ob'ektga bo'lgan kuchli havolalar soni 0 ga tushganda, u xotiradan o'chiriladi (deinit).
| Havola turi | Xotira xatti-harakati | Qachon ishlatiladi |
|---|---|---|
**strong** | Hisoblagichni 1 ga oshiradi | Ob'ektga egalik qilganda (sukut bo'yicha) |
**weak** | Hisoblagichni oshirmaydi; ob'ekt o'chganda avtomatik nil bo'ladi | Delegatlar, klojurlar, doimo ixtiyoriy (Optional) bo'ladi |
**unowned** | Hisoblagichni oshirmaydi; ob'ekt o'chganda nil bo'lmaydi | Ikki ob'ekt bir vaqtda yashab, bir vaqtda o'lishiga 100% ishonch bo'lganda |
Ogohlantirish: Agar ob'ekt xotiradan o'chib ketgandan keyin unga
unownedorqali murojaat qilinsa, ilova darhol qulaydi:Fatal error: Attempted to read an unowned reference but the object was already deallocated.
2. O'lchangan faktlar: Klojurlar va [weak self] {#closures}
iOS 26.5 simulyatorida XCTest orqali o'tkazilgan eksperiment natijalari (executed_facts.md):
import Foundation
final class DataService {
var onCompletion: (() -> Void)?
}
final class StrongOwner {
let service = DataService()
init() {
// XATO: self kuchli ushlab qolinadi:
service.onCompletion = { self.updateUI() }
}
func updateUI() {}
}
final class WeakOwner {
let service = DataService()
init() {
// TO'G'RI: [weak self] siklni buzadi:
service.onCompletion = { [weak self] in self?.updateUI() }
}
func updateUI() {}
}
O'lchov natijasi:
closure captures self strongly -> owner freed after scope: false(Xotirada abadiy qolib ketadi!).closure captures [weak self] -> owner freed after scope: true(Xotiradan to'g'ri bo'shatiladi).
3. Delegatlar: strong delegate xatosi {#delegates}
Delegat xususiyatini e'lon qilishda eng ko'p uchraydigan xato — weak so'zini unutishdir:
import Foundation
// 1. Protokol AnyObject'dan meros olishi shart:
public protocol PaymentViewDelegate: AnyObject {
func paymentDidComplete()
}
public final class PaymentView {
// TO'G'RI: weak bo'lishi shart!
public weak var delegate: PaymentViewDelegate?
public init() {}
}
O'lchangan fakt:
- Agar delegat
strongbo'lsa ->screen freed: false(View Controller ekrandan ketsa ham xotirada qoladi). - Agar delegat
weakbo'lsa ->screen freed: true.
4. Timer va NotificationCenter xotira tuzoqlari {#timer-notifications}
1. Timer tuzog'i
Klassik Timer.scheduledTimer(timeInterval:target:selector:...) o'zining target parametrini Run Loop ichida kuchli (strong) ushlab turadi.
O'lchangan fakt:
- Timer ishlayotgan paytda controller bo'shatilmaydi (
owner freed: false). - Faqat
timer.invalidate()chaqirilgandagina ob'ekt xotiradan bo'shatiladi (after timer.invalidate() -> owner freed: true)!
Zamonaviy va xavfsiz blokli Timer:
import Foundation
public final class PollingManager {
private var timer: Timer?
public func start() {
// [weak self] bilan blokli Timer Run Loop siklini oldini oladi:
timer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { [weak self] _ in
self?.pingServer()
}
}
public func stop() {
timer?.invalidate()
timer = nil
}
private func pingServer() {}
deinit {
timer?.invalidate()
}
}
2. NotificationCenter tuzog'i
- Selector-based:
NotificationCenter.default.addObserver(self, selector: ...)iOS 9 dan boshlab zeroing-weak reference ishlatadi, shuning uchundeinitdaremoveObserverqilmasa ham retain cycle bo'lmaydi (observer freed: true). - Block-based:
addObserver(forName:queue:using:)esa o'z ichidaselfni kuchli ushlab qolsa, **retain cycle hosil qiladi (observer freed: false)**! Buning oldini olish uchun token saqlanishi vadeinitda olib tashlanishi shart.
5. Copy-on-Write (CoW) qanday ishlaydi? {#cow}
Swift'dagi massivlar (Array), lug'atlar (Dictionary) va qatorlar (String) qiymatli turlar (struct) hisoblanadi. Biroq har safar massiv boshqa o'zgaruvchiga berilganda xotira darhol nusxalanmaydi.
O'lchangan ilmiy natija:
import Foundation
func testCopyOnWrite() {
var a = Array(0..<1000)
let b = a
// 1. Mutatsiyadan oldin a va b bitta xotira manziliga qaraydi:
// Array copy shares storage before mutation: true
a[0] = 99
// 2. Yozish (mutatsiya) sodir bo'lgan paytda a uchun alohida nusxa ochiladi:
// after mutating one copy still shared: false; b[0] = 0
}
Bu Copy-on-Write deb ataladi. U xotirani vaqtinchalik nusxalashdan asraydi va faqat o'zgarish kiritilgan nusxa uchun yangi RAM ajratadi.
6. Autoreleasepool: xotira cho'qqilarini (spikes) yo'qotish {#autoreleasepool}
Katta hajmdagi sikllarda (masalan, 1,000 ta rasmni qayta ishlash) vaqtinchalik yaratilgan CoreGraphics yoki Objective-C ob'ektlari Run Loop tugamaguncha xotirada to'planib qoladi. Natijada xotira birdan $500\text{ MB}$ ga sakrab, ilova qulaydi.
Buning yechimi — **autoreleasepool**:
import Foundation
func processBatchImages(urls: [URL]) {
for url in urls {
autoreleasepool {
// Har bir aylanmada hosil bo'lgan vaqtinchalik xotira
// blok yakunlanishi bilan darhol tozalanadi:
let _ = try? Data(contentsOf: url)
}
}
}
7. Xotira oqishini topish: Memory Graph va Instruments {#diagnostics}
Retain cycle'larni topishning 3 ta asosiy yo'li:
- **
deinitichida log qo'yish:** Har bir controller va servisdadeinit { print("\(Self.self) bo'shatildi") }bo'lishi kerak. Agar ekrandan chiqqanda bu yozuv chiqmasa — retain cycle bor. - Xcode Memory Graph Debugger: Pastki panelda 3 ta doiracha belgisi. U ob'ektlar o'rtasidagi kuchli havolalar zanjirini vizual ko'rsatadi va shubhali sikllarni binafsha rangli ogohlantirish bilan belgilaydi.
- Instruments (Leaks & Allocations): Ilovani profayl qilish uchun eng kuchli vosita bo'lib, xotira hajmining o'sish dinamikasini real vaqt rejimida qayd etadi.
8. Xulosa {#xulosa}
- Retain cycle ikkita ob'ekt bir-birini kuchli (strong) ushlab turganda yuzaga keladi.
- Klojurlarda
[weak self]ishlatish va delegatlarniweakqilish xotira xavfsizligining oltin qoidasidir. unownedfaqat ikkinchi ob'ekt birinchisidan oldin hech qachon o'chmasligiga 100% kafolat bo'lganda qo'llanilishi kerak, aks holda crash beradi.- Blokli NotificationCenter va Target-Action Timer doimo qo'lda bekor qilinishi (
invalidate/removeObserver) shart. - Swift CoW orqali faqat ma'lumot o'zgargandagina yangi xotira ajratadi.
- Sikllardagi xotira cho'qqilarini yo'qotish uchun
autoreleasepooldan foydalaniladi.