iOS Swift Performans Master Serisi Part 2: “deinit” Neden Çalışmaz? Kapsamlı Senaryolar.
iOS “deinit” Neden Çalışmaz?
“deinit” Neden Çalışmaz? Kapsamlı Senaryolar: iOS Swift Performans Master Serisi Part 2
1. “deinit” Nedir?
deinit, bir class instance’ı bellekte tamamen serbest bırakılmadan hemen önce çağrılan özel bir metottur.
class Example {
deinit {
print("Example deinit")
}
}
deinitmanuel çağrılmaz.deinitARC tarafından otomatik tetiklenir.deinitçağrılıyorsa: Nesneye ait retain count = 0 olmuştur.
Eğer
deinitçalışmıyorsa, bu bir hata değil; ARC’nin hâlâ o nesneye ihtiyaç duyduğunu düşündüğünün kanıtıdır.
2. “deinit" Neden Çalışmaz? (Temel Sebep)
Tek ve değişmez sebep:
Nesneye hâlâ en az bir strong reference vardır.
Tüm senaryolar bu cümlenin etrafında şekillenir.
deinit:Debug print değildir. “Çalıştı mı çalışmadı mı” kontrolü değildir.
deinit çalışmıyorsa:
- Uygulama gereksiz bellek tutuyordur.
- Performans düşer.
- Scroll, animasyon ve network yavaşlar.
3. En Yaygın Senaryolar
3.1 Retain Cycle (Strong Reference Cycle)
Hatırlayalım: Retain Cycle, iki veya daha fazla nesnenin birbirini strong referansla tutması sonucu, ARC’nin hiçbirini serbest bırakamaması durumudur. Detaylı bilgiye Part 1'den erişebilirsiniz.
Örnek:
class A {
var b: B?
}
class B {
var a: A?
}
Burada:
- A → B’yi tutar
- B → A’yı tutar
Sonuç:
- Her iki nesnenin retain count’u asla 0 olmaz.
deinitçağrılmaz.
Çözüm:
İlişkinin sahip olmayan tarafı weak veya unowned olmalıdır.
class B {
weak var a: A?
}
3.2 ViewController — ViewModel İlişkisi (MVVM Mimarisi)
Problem:
class ViewModel {
var viewController: MyVC
}
- VC → ViewModel
- ViewModel → VC
UI kapatılsa bile:
- Nesneler birbirini tutmaya devam eder.
Doğru Model:
class ViewModel {
weak var viewController: MyVC?
}
ViewModel, ViewController’ın sahibi değildir.
3.3 Closure’lar (En Sinsi Senaryo)
Closure’lar, tanımlandıkları scope’taki değişkenleri yakalayarak (capture) saklar.
Hatırlayalım: Class instance’ları strong olarak yakalanır.
Problemli Örnek:
class MyVC {
func load() {
service.fetch {
self.updateUI()
}
}
}
Burada:
- VC → service
- service → closure
- closure → self (VC)
Çözüm: Capture List
service.fetch { [weak self] in
self?.updateUI()
}
3.4 Timer
Timer, belirli aralıklarla çalışan bir mekanizmadır.
Bir kez başlatıldığında:
- Kendi kendine durmaz.
- Uygulamanın çalışma döngüsüne (RunLoop) eklenir
- Süresi doldukça tekrar tekrar tetiklenir.
Timer, target’ını strong tutar.
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
self.tick()
}
Bu kod:
- Her 1 saniyede bir çalışır.
- Uygulama kapanana kadar devam eder.
Timer çalıştığı sürece:
- VC serbest bırakılamaz.
Çözüm:
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
self?.tick()
}
Artık ekran kapandığında:
selfartık yoktur (nil).tick()çağrılmaz.- Uygulama çökmez.
- Bellek sızıntısı olmaz.
Bu yüzden:
“Her şey çalışıyor gibi” görünür.
Ama perde arkasında ne oluyor?
- Timer hâlâ çalışıyor.
- Her saniye tetikleniyor.
- Closure çağrılıyor.
- İçeride hemen çıkılıyor.
Yani:
- Gereksiz işlem yapılıyor.
- Küçük de olsa CPU ve enerji harcanıyor.
Tek bir timer için bu fark edilmez. Ama: Birçok ekranda birçok timer varsa bu durum birikerek sorun hâline gelir.
**invalidate() ne yapar?**
invalidate() timer’a şunu söyler:
“Artık görevini tamamladın, durabilirsin.”
Teknik olarak:
- Timer RunLoop’tan çıkarılır.
- Bir daha tetiklenmez.
- Tamamen etkisiz hâle gelir.
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
timer?.invalidate()
timer = nil
}
Bu çağrıdan sonra:
- Timer artık çalışmaz.
- Tekrar başlatılmadan geri gelmez.
3.5 Singleton’lar
Singleton’lar uygulama boyunca yaşayan nesnelerdir.
Örnek:
class Manager {
static let shared = Manager()
}
Eğer bu singleton bir ViewController’ı strong referansla tutarsa:
class Manager {
static let shared = Manager()
var delegate: SomeViewController?
}
ve bir yerde:
Manager.shared.delegate = self
olursa:
- Singleton → VC (strong).
- Singleton hiç ölmez.
- VC de hiçbir zaman serbest bırakılamaz.
Sonuç:
deinitçalışmaz.- Memory leak oluşur.
- Ekran kapansa bile VC bellekte kalır.
Çözüm:
Singleton UI nesnesi tutmamalı.
Singleton’ın sorumluluğu:
- Veri
- İş mantığı
- Servis yönetimi
Ekran, View, ViewController değil.
Eğer gerçekten bir VC’ye işaret etmesi gerekiyorsa:
class Manager {
static let shared = Manager()
weak var delegate: SomeViewController?
}
Bu sayede:
- VC kapanınca
nilolur. - Singleton hayatta kalır.
- Leak oluşmaz.
3.6 Combine / RxSwift
Combine’da Publisher ve Subscriber arasındaki bağlantı, bir Subscription üzerinden kurulur.
Bu subscription aktif olduğu sürece akış devam eder.
Eğer bir Publisher’a yapılan subscription iptal edilmezse:
publisher
.sink { value in
self.handle(value)
}
- Publisher → Subscriber (strong).
- Subscriber → ViewController (strong).
- Subscription aktif kaldığı sürece zincir kopmaz.
Özellikle Subject veya hiç complete etmeyen publisher’larda:
- Subscription kendiliğinden bitmez.
- ViewController serbest bırakılamaz.
Çözüm:
Subscription’lar mutlaka ViewController yaşam döngüsüne bağlanmalıdır.
publisher
.sink { [weak self] value in
self?.handle(value)
}
.store(in: &cancellables)
Bu sayede:
weak selfile retain cycle kırılır.cancellablesdeinit olunca tüm subscription’lar otomatik olarakcancel()edilir.- ViewController serbest bırakılabilir.
3.7 Async / Await & Task
Task {
await self.load()
}
Task:
- self’i strong tutar.
Çözüm:
Task { [weak self] in
await self?.load()
}
veya:
Task.cancel()
4. Altın Kurallar
4.1 “Bunun Sahibi Kim?” Sorusu
Her class için sorulması gereken en kritik soru budur:
Bu nesnenin yaşam süresini kim kontrol ediyor?
Sahiplik (Ownership) Ne Demektir?
Bir nesnenin sahibi, onun:
- Ne zaman oluşturulacağına
- Ne zaman yok edileceğine
dolaylı veya doğrudan karar veren yapıdır.
ARC açısından sahiplik şudur:
- Sahip olan →
strongreferans tutar. - Sahip olmayan →
weakveyaunownedtutar.
Yanlış Sahiplik Örneği
class ViewModel {
var viewController: MyViewController
}
Burada ViewModel şunu iddia eder:
“Ben ViewController’ın sahibiyim”
Bu mantıksal olarak yanlıştır, çünkü:
- ViewController ekrana gelir.
- ViewController gider.
- ViewModel onunla birlikte ölmelidir.
Doğru Sahiplik Modeli
class ViewModel {
weak var viewController: MyViewController?
}
Altın Kural:
UI katmanı genellikle sahip olunan taraftır, sahip olan değil.
4.2 Closure Yazarken Refleks: [weak self]
- Closure’ın ne kadar yaşayacağını bilemezsin.
- Network gecikir.
- Async işlem uzar.
- Task askıda kalır.
Doğru Refleks
service.fetch { [weak self] in
self?.updateUI()
}
4.3 "deinit" Görmüyorsan Ne Yapmalısın?
deinit çalışmıyorsa:
- Tahmin yürütme.
- Koddan çıkarmaya çalışma.
Doğru Araç: Memory Graph Debugger
Memory Graph Debugger sana şunu gösterir:
- Hangi nesne
- Hangi nesneyi
- Hangi referans türüyle tutuyor
Nasıl Düşünmelisin?
- ViewController kapatılır.
deinitbeklenir
Gelmiyorsa:
- Kim tutuyor?
- Strong mu?
- Gereksiz mi?
*deinitgelmiyorsa sorun orada değil, onu tutan yerdedir.*
Bu bölümle birlikte iOS Performans iyileştirme anlamında en temel ve önemli konuları öğrendik, temel senaryoları önleyebilir hale geldik. Serinin diğer bölümlerinde daha üst seviyelere çıkarak bütün kapsamı tamamlayacağız. 🚀
메타데이터
- post_id
- c5b5a3fdc467
- slug
- ios-swift-performans-master-serisi-part-2-deinit-neden-çalışmaz-kapsamlı-senaryolar-c5b5a3fdc467
- url
- https://medium.com/@kadiroruc/ios-swift-performans-master-serisi-part-2-deinit-neden-%C3%A7al%C4%B1%C5%9Fmaz-kapsaml%C4%B1-senaryolar-c5b5a3fdc467
- canonical_url
- https://medium.com/@kadiroruc/ios-swift-performans-master-serisi-part-2-deinit-neden-%C3%A7al%C4%B1%C5%9Fmaz-kapsaml%C4%B1-senaryolar-c5b5a3fdc467
- author_url
- https://medium.com/@kadiroruc
- status
- ok
- fetched_at
- 2026-07-13 06:23:13