12. Delegation va Retain Cycles: weak, ARC va Instruments Memory Graph
🏛️ iOS Design Patterns — Mukammal Enterprise & Architecture Qo‘llanma
12. Apple Native: Delegation Pattern va Retain Cycle Qotillari
"Apple ekotizimida eng ko'p xotira oqishiga (Memory Leak) sabab bo'ladigan bitta so'z bor: u ham bo'lsa 'weak' so'zining unutilishidir. Delegation — bu Apple arxitekturasining yuragi, ammo ehtiyotsiz qo'llarda xotira qotilidir."
1. Delegation Pattern Nima va U Qanday Ishlaydi?
Delegation (Vakillik) — bu bir obyekt (Delegator) o'zida yuz bergan voqeani boshqa bir obyektga (Delegate) xabar qilish yoki biror qaror qabul qilishni unga topshirish imkonini beruvchi pattern.
sequenceDiagram
autonumber
participant VC as ProfileViewController (Delegate)
participant View as CustomAvatarPickerView (Delegator)
VC->>View: 1. view.delegate = self
Note over View: Foydalanuvchi yangi rasm tanladi!
View->>VC: 2. delegate?.avatarPicker(didSelect: image)
VC-->>VC: 3. Rasmni serverga yuklash logikasini yurgizadi
Apple'ning Mashhur Delegatlari:
UITableViewDelegate— "Foydalanuvchi 4-qatorni bosdi, nima qilay?"CLLocationManagerDelegate— "Yangi GPS koordinata keldi, oling!"WKNavigationDelegate— "Veb sahifa ochilishni boshladi, ruxsat berasizmi?"
2. Retain Cycle Fojiasi: "weak" Nega Majburiy?
iOS da xotira ARC (Automatic Reference Counting) orqali boshqariladi. Obyektning Reference Count (kuchli havolalar soni) 0 ga teng bo'lgandagina u xotiradan (Heap) o'chiriladi.
flowchart LR
subgraph Leak["❌ WEAK UNUTILSA: Retain Cycle (Xotira Oqishi)"]
direction LR
VC1["ViewController\n(Ref Count: 1)"] -->|"strong view"| V1["CustomView\n(Ref Count: 1)"]
V1 -->|"strong delegate"| VC1
Note1["💥 Ekranni yopganda ikkalasining ham\nRef Count'i 1 bo'lib qolaveradi!\ndeinit hech qachon chaqirilmaydi!"]
end
subgraph Fixed["✅ WEAK BILAN: Toza Xotira"]
direction LR
VC2["ViewController\n(Ref Count: 1)"] -->|"strong view"| V2["CustomView\n(Ref Count: 1)"]
V2 -.->|"weak delegate (0 qo'shmaydi)"| VC2
Note2["🚀 Ekran yopilganda ikkalasi ham\nxotiradan 100% o'chiriladi!"]
end
style Leak fill:#251014,stroke:#f87171,stroke-width:1px
style Fixed fill:#0d1f14,stroke:#4ade80,stroke-width:1px
3. Real Swift 6 Kodi: Xavfsiz Delegat
import UIKit
// 1. Delegat Protokoli (Doimo ': AnyObject' bo'lishi shart, aks holda weak qo'yib bo'lmaydi!)
public protocol CustomInputViewDelegate: AnyObject, Sendable {
func inputViewDidSubmit(text: String)
func inputViewDidCancel()
}
// 2. Delegator (View)
public final class CustomInputView: UIView {
// ⚠️ ENG MUHIM SO'Z: 'weak'
public weak var delegate: (any CustomInputViewDelegate)?
public func submitTapped(text: String) {
delegate?.inputViewDidSubmit(text: text)
}
public func cancelTapped() {
delegate?.inputViewDidCancel()
}
deinit {
print("🗑 CustomInputView xotiradan muvaffaqiyatli o'chirildi!")
}
}
// 3. Delegate (ViewController)
public final class RegistrationViewController: UIViewController, CustomInputViewDelegate {
private let inputView = CustomInputView()
public override func viewDidLoad() {
super.viewDidLoad()
// O'zimizni delegat qilib tayinlaymiz
inputView.delegate = self
view.addSubview(inputView)
}
public func inputViewDidSubmit(text: String) {
print("Kiritilgan matn: \(text)")
}
public func inputViewDidCancel() {
dismiss(animated: true)
}
deinit {
print("🗑 RegistrationViewController xotiradan muvaffaqiyatli o'chirildi! (No Leak)")
}
}
4. Retain Cycle'ni Xcode da Qanday Aniqlash Mumkin?
Senior dasturchi har bir yangi ekranni yozgach, uni xotirada qolib ketmaganini tekshiradi:
- **
deinittekshiruvi:** Har bir ViewController'gadeinit { print("🗑 Deallocated") }yozib ko'ring. Ekranni yopganingizda ushbu log chiqmasa — demak Retain Cycle bor! - Xcode Memory Graph Debugger: Xcode'ning pastki panelidagi 3 ta doiracha belgisini bosing. Agar ViewController ustida qizil belgi turgan bo'lsa va u hali ham xotirada yashayotgan bo'lsa, havolalar zanjirini grafikda ko'rsatadi.
5. Interaktiv Savol-Javob (Self-Check Quiz)
<details> <summary><b>1-Savol: Nega Swift'da faqat 'AnyObject' (Class) ga cheklangan protokollargagina 'weak' qo'yish mumkin?</b> (Yechimni ko'rish uchun bosing)</summary>
Tahlil va Yechim: Chunki weak faqat Reference Type (Class) lar uchun ishlaydi — ARC xotira hisoblagichiga murojaat qiladi. Struct va Enum lar esa Value Type bo'lib, ular Heap'da emas, Stack'da saqlanadi va ularda Reference Counting bo'lmaydi. Shuning uchun protocol MyDelegate: AnyObject deb yozilmasa, Swift kompilyatori weak cannot be applied to non-class-bound protocol xatosini beradi. </details>
<details> <summary><b>2-Savol: Delegation va Closure (Callback) o'rtasida qachon qaysi birini tanlash kerak?</b> (Yechimni ko'rish uchun bosing)</summary>
Tahlil va Yechim:
- Closure: Agar faqat 1 ta oddiy hodisa (masalan, tugma bosildi yoki ma'lumot keldi) bo'lsa, Closure yozish oson va qisqa.
- Delegation: Agar voqealar soni 3 tadan ko'p bo'lsa (masalan: boshlandi, xato bo'ldi, tugadi, progress o'zgardi, ruxsat so'raldi), Delegation tanlanadi. U barcha metodlarni bitta tartibli protokolga jamlaydi.
</details>
6. Amaliy Topshiriq (Senior Challenge)
AudioRecorderService sinfini yarating:
- Uning
AudioRecorderDelegatenomli protokoli bo'lsin:didStartRecording(),didUpdateVolumeLevel(dB: Float),didFinishRecording(url: URL). - Ushbu xizmatni ViewModel bilan zaif havola (
weak delegate) orqali bog'lang va konsoldadeinitchaqirilishini tekshiring!