Strong Reference (güclü istinad) — bu, obyektin yaddaşda qalmasını təmin edən standart yaddaş idarəetmə mexanizmidir, ona ən azı bir aktiv istinad göstərdikcə. Zəif istinadlardan fərqli olaraq, güclü istinad obyektin istinad sayğacını artırır və onun avtomatik azad olmasının qarşısını alır. Apple Developer Documentation-ya görə, ARC Swift və Objective-C-də obyektlərin həyat müddətini avtomatik idarə edir. Güclü istinadların işini başa düşmək mobil tətbiqlərdə yaddaş sızmalarının və siklik asılılıqların qarşısını almaq üçün kritik əhəmiyyət daşıyır.
Əsas məqamlar
Strong Reference — bu, obyektin zibil yığan və ya yaddaş idarəetmə sistemi tərəfindən məhv edilməsinin qarşısını alan istinad növüdür. Obyektə ən azı bir güclü istinad mövcud olduqca, onun altındakı yaddaş azad edilmir. Bu, Swift və Objective-C-də ARC-nin və Java və Kotlin-də garbage collection-un əsasında duran əsas mexanizmdir.
Güclü istinad konsepsiyası avtomatik yaddaş idarəçiliyi olan bütün dillər üçün fundamental əhəmiyyət daşıyır. ARC olan sistemlərdə hər bir güclü istinad obyektin istinad sayğacını artırır. Sayğac sıfıra düşdükdə, obyekt dərhal dealokasiya edilir. Java və Kotlin-də garbage collector ilə güclü istinad obyektin əldə edilə bilən olduğunu və GC tərəfindən yığılmayacağını təmin edir.
WWDC 2021 məlumatlarına görə, iOS tətbiqlərində yaddaş sızmalarının təxminən 35%-i güclü istinadların düzgün istifadə edilməməsi və saxlama dövrləri ilə bağlıdır. Android inkişafında bağlanmalar və geri çağırışlarda gizli strong reference vasitəsilə sızmalar Context Leak-dan sonra yaddaş problemlərinin ikinci ən çox yayılmış səbəbidir.
Yaddaşla effektiv işləmək üçün strong, weak və unowned istinadları arasındakı fərqi başa düşmək və obyektlərin sahibliyi və həyat müddətindən asılı olaraq istinad növünü düzgün seçmək lazımdır.
ARC tətbiq edilməzdən əvvəl tərtibatçılar hər bir obyekt üçün əl ilə retain və release çağırırdılar, bu da çoxsaylı səhvlərə səbəb olurdu. Apple tərəfindən 2011-ci ildə LLVM 3.0 buraxılışı ilə təqdim edilən ARC, kompilasiya mərhələsində sahiblik qrafikini təhlil edərək bu prosesi avtomatlaşdırdı. Kompilyator özü retain, release və autorelease çağırışlarını lazımi yerlərə daxil edir.
Clang Static Analyzer-ə görə, ARC-nin tətbiqi iOS tətbiqlərində yaddaşla bağlı səhvlərin sayını 70% azaldıb. Tərtibatçı üçün bu o deməkdir ki, yaddaş idarəçiliyi daha təhlükəsiz olub, lakin eyni zamanda güclü istinadların arxa planda necə işlədiyini başa düşmək zərurəti yaranıb — retain cycles-dən qaçmaq üçün.
Kotlin və Java-da ARC rolunu garbage collector yerinə yetirir, lakin güclü istinad prinsipi eyni qalır: GC Roots — bunlar obyektlərin güclü istinadlarla saxlandığı giriş nöqtələridir. Obyekt GC Root-dan güclü istinadlar zənciri ilə əldə edilə bildikcə, o yığılmayacaq.
ARC (Automatic Reference Counting) yığındakı hər bir obyekt üçün istinadların sayılması prinsipi ilə işləyir. Obyektə yeni güclü istinad yaradıldıqda, sayğac artır (retain). İstinad məhv edildikdə və ya üzərinə yazıldıqda, sayğac azalır (release). Sayğac sıfıra çatdıqda, obyekt dərhal yaddaşdan silinir.
Swift-də bir nümunəyə baxaq. Sinif nümunəsi yaradılarkən ARC yaddaş ayırır və retain count-ni 1-ə bərabər qoyur. Hər yeni dəyişənə təyinat sayğacı artırır. Dəyişən görünmə sahəsindən çıxdıqda, sayğac azalır:
class ProfileViewController {
var nameLabel: String?
var avatarImage: UIImage?
func loadProfile() {
// retain count = 1 yeni nümunə üçün
let user = User(name: "Ivan")
// retain count = 2 nameLabel təyin edildikdən sonra
nameLabel = user.name
// metoddan çıxış — user scope-dan çıxır, retain count = 1
}
}
Bu kodda ARC User obyektinin yaddaşda qalmasını təmin edir, ona ən azı bir güclü istinad olduqca. loadProfile funksiyası başa çatdıqda, yerli user dəyişəni məhv edilir, lakin nameLabel hələ də obyekti saxlayır. Yaddaş yalnız nameLabel mövcud olmağı dayandırdıqda və ya üzərinə yazıldıqda azad ediləcək.
Kotlin-də oxşar davranış GC Roots vasitəsilə təmin edilir. Zibil yığanın kökündən (məsələn, statik sahə və ya aktiv thread) izlənilə bilən strong references zənciri mövcud olduqca, obyekt yaddaşda qalır. Fərq ondadır ki, GC yaddaşı dərhal azad etmir — bu, əldə edilə bilmə təhlilindən sonra asinxron şəkildə baş verir.
ARC-də azadetmə sayğac sıfırlandığı anda sinxron şəkildə baş verir. Swift və Objective-C-də obyektin nə vaxt silinəcəyini dəqiq bilirsiniz. Kotlin və Java-da azadetmə anı gözlənilməzdir, lakin bu, garbage collector səviyyəsində siklik asılılıqların aşkarlanmasının daha çevik sxemi ilə kompensasiya olunur.
Retain cycle (saxlama dövrü) — iki və ya daha çox obyektin bir-birinə qarşılıqlı güclü istinadlara malik olduğu vəziyyətdir. Nəticədə onların retain count-ni heç vaxt sıfıra düşmür və obyektlər tətbiq üçün lazımsız hala gəlsələr belə, yaddaş azad edilmir.
Klassik nümunə: valideyn view controller uşaq obyekti güclü istinadla saxlayır, o da öz növbəsində valideyni güclü istinadla saxlayır. Bu, deleqatlar, bağlanmalar və iç-içə lambda ifadələri olan vəziyyətlər üçün xarakterikdir. Instruments Leaks-ə görə, retain cycles ARC istifadə edən tətbiqlərdə bütün yaddaş sızmalarının 60%-ə qədərini təşkil edir.
class ParentViewController: UIViewController {
var child: ChildViewController?
func setupChild() {
child = ChildViewController()
// retain cycle: parent child-i saxlayır, child parent-i closure vasitəsilə saxlayır
child?.onEvent = {
self.handleEvent()
}
}
func handleEvent() {}
}
Problem ondadır ki, onEvent bağlanması self-i (ParentViewController) güclü istinadla tutur, ParentViewController isə child-i güclü istinadla saxlayır. Hər iki obyekt heç vaxt azad edilməyəcək. Həll yolu — dövrü qırmaq üçün bağlanmada weak self istifadə etməkdir.
Kotlin-də oxşar dövrlər xarici obyektləri tutan lambdalardan istifadə edərkən yaranır. JVM garbage collector zamanla belə dövrləri aşkar edə bilər, ancaq obyektlər GC Roots-dan əldə edilə bilməz olduqda. Əgər dövr aktiv thread və ya UI konteksti ilə bağlıdırsa, sızma tətbiqin bütün ömrü boyu qalır.
İstinad növləri arasındakı fərqi başa düşmək təhlükəsiz yaddaş idarəçiliyinin açarıdır. Strong Reference retain count-ni artırır. Weak Reference retain count-ni artırmır və obyekt azad edildikdə avtomatik nil olur. Unowned Reference də retain count-ni artırmır, lakin sıfırlanmır — azad edildikdən sonra ona müraciət crash-ə səbəb olur.
| İstinad növü | Retain count | Təhlükəsizlik | Nə vaxt istifadə olunmalı |
|---|---|---|---|
| Strong | +1 | Təhlükəsiz (standart) | Obyektə sahiblik, parent → child münasibəti |
| Weak | Dəyişmir | Avtomatik sıfırlanma (safe) | Deleqatlar, callback, geri istinadlar |
| Unowned | Dəyişmir | Gec müraciətdə crash riski | Obyekt sahibdən daha uzun yaşamayacaqsa |
İstinad növünün seçimi sahiblik münasibəti ilə müəyyən edilir. Əgər B obyekti A-nın hissəsidirsə və onsuz mövcud ola bilmirsə — Strong istifadə edin. Əgər B müstəqil mövcud ola bilirsə və bildirişlər üçün A-ya istinad edirsə — Weak istifadə edin. Unowned nadir hallarda tətbiq olunur — yalnız uşaq obyektin ömrü valideynin ömrünü qəti şəkildə aşmadıqda.
Apple Developer Documentation tövsiyə edir: standart olaraq bütün sahiblik münasibətləri üçün strong istifadə edin. Retain cycle-dən qaçmaq lazımdırsa — hansı istinadın zəif olmalı olduğunu müəyyən edin. Adətən bu, iyerarxiyada geri istinaddır (child → parent). Kotlin-də oxşar rol cache və observer nümunələri üçün tətbiq olunan java.lang.ref-dən WeakReference-ə aiddir.
Retain cycles aşkarlanması — ilk addım. İkincisi — onların düzgün aradan qaldırılması. Güclü istinad dövrləri ilə mübarizənin əsas vasitəsi istinadlardan birini weak və ya unowned ilə əvəz etməkdir. Zibil yığımı olan dillərdə əlavə olaraq hər girişdən əvvəl əl ilə null yoxlaması ilə WeakReference tətbiq olunur.
Swift və Objective-C-də ən çox yayılmış düzəliş bağlanmalara [weak self] əlavə etməkdir. Bu, bağlanmanın obyekti azad edildikdən sonra saxlamamasını təmin edir. Kotlin-də oxşar məqsədlər üçün WeakReference sarğısı və ya onDestroy-də istinadın açıq təmizlənməsi istifadə olunur.
class NetworkService {
func fetchData(completion: @escaping (Data?) -> Void) {
// weak self vasitəsilə tutma — retain cycle istisna edilib
URLSession.shared.dataTask(
with: URL(string: "https://api.example.com")!
) { [weak self] data, response, error in
guard let self else { return }
completion(data)
}.resume()
}
}
Bu nümunədə [weak self] NetworkService-in artıq lazım olmadıqdan sonra bağlanma tərəfindən saxlanmamasını təmin edir. Əgər self sorğu tamamlanmadan əvvəl azad edilibsə — guard let self else { return } completion-i çağırmadan bağlanmadan çıxır.
Retain cycles diaqnostikası üçün iOS üçün Instruments Leaks və ya Android üçün Android Profiler + LeakCanary istifadə edin. Bu alətlər dəqiq saxlama qrafikini göstərir və obyektin azad edilməsinə hansı güclü istinadın mane olduğunu bildirir. Müntəzəm yaddaş profilləşdirməsi hər bir mobil layihənin CI/CD pipeline-ının bir hissəsi olmalıdır.
Swift və Kotlin prinsipial olaraq fərqli yaddaş idarəetmə mexanizmlərindən istifadə edir, lakin güclü istinad konsepsiyası hər ikisində mövcuddur. Swift-də retain count = 0 olduqda sinxron azadetmə ilə ARC tətbiq olunur. Kotlin-də izləyən GC istifadə olunur, o, əldə edilə bilməyən obyektləri asinxron təmizləyir.
| Parametr | Swift (ARC) | Kotlin (JVM GC) |
|---|---|---|
| Mexanizm | İstinadların sayılması (retain count) | Əldə edilə bilmənin izlənməsi (GC Roots) |
| Azadetmə | Sinxron (sayğac sıfırlandıqda) | Asinxron (GC dövrü ilə) |
| Retain cycle | Avtomatik aşkarlanmır | GC aşkar edə bilər, lakin dərhal deyil |
| Weak ref | weak (avtomatik sıfırlanma) | WeakReference (əl ilə yoxlama) |
Əsas praktik fərq: Swift-də retain cycle — zəmanətli sızmadır. Kotlin-də GC obyektlər kökdən əldə edilə bilməz olarsa, dövrü qıra bilər, lakin sızan obyektlərin ömrü gözlənilməz qalır. Buna görə hər iki dildə ən yaxşı strategiya dizayn mərhələsində güclü istinad dövrlərindən qaçmaqdır.
Swift üçün delegate nümunələrində və bağlanmalarda weak istifadə edin. Kotlin üçün — WeakReference və ya sahib məhv edildikdə istinadları avtomatik təmizləyən Lifecycle-aware komponentləri. Hər iki yanaşmada məqsəd birdir — qırılmaz saxlama zənciri yaratdıqları yerlərdə güclü istinadları istisna etmək.
Tez-tez verilən suallar
Strong Reference obyektin retain count-ni artırır və istinad mövcud olduqca onun azad edilməsinin qarşısını alır. Weak Reference retain count-ni dəyişmir və obyekt yaddaşdan silindikdə avtomatik sıfırlanır. Güclü istinadlar sahiblik üçün, zəif istinadlar isə geri əlaqələr və deleqatlar üçün istifadə olunur.
Retain cycle — iki obyektin bir-birini güclü istinadlarla saxladığı qarşılıqlı blokadadır. Onların retain count-ni heç vaxt sıfıra düşmür, yaddaş azad edilmir. Bu, yaddaş sızmasına gətirib çıxarır: obyektlər əbədi olaraq yığında qalır, tətbiq getdikcə daha çox resurs istehlak edir və nəticədə OutOfMemory ilə çökür.
Xcode-dan Instruments Leaks istifadə edin — Leaks şablonu ilə profilləşdirməyə başlayın, tətbiqdə ssenarini icra edin və sızma göstəricilərini yoxlayın. Dəqiq diaqnostika üçün Cycles & Roots sekmesine keçin — o, qırılmayan dövr təşkil edən qarşılıqlı strong reference qrafikini göstərəcək.
Unowned-dən uşaq obyektin ömrü valideynin ömrünü qəti şəkildə aşmadıqda istifadə edin — məsələn, obyekti ciddi şəkildə müəyyən edilmiş əhatə dairəsinə bağlayarkən. Şübhə varsa, Weak istifadə edin, çünki azad edilmiş unowned-a müraciət tətbiqin çökməsinə səbəb olur.
Dolayı yolla — bəli. ARC-də hər bir retain və release əlavə xərcləri olan atomik əməliyyatdır. Dövrlərdə çox sayda obyekt olduqda bu, performansa təsir edə bilər. Lakin əsas problem ARC-nin iş sürəti deyil, səhv seçilmiş istinad növü səbəbindən yaddaş sızmalarıdır.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun