SwiftUI eğitiminde kalıcı ekran, Task ve closure referanslarını Instruments ile teşhis edin. iOS MVVM mimarisinde ölçülebilir bellek tabanı oluşturup gerçek retain cycle sorunlarını giderin.
SwiftUI Eğitiminde Bellek Sızıntılarını Instruments ile İzleme
SwiftUI Eğitimi İçin Ölçülebilir Bellek Tabanı Oluşturma
Bir swiftui eğitimi veya üretim projesinde “ekrandan çıkınca bellek düşüyor mu?” sorusunu yalnızca Xcode bellek göstergesine bakarak yanıtlamayın. Xcode Instruments içindeki Allocations şablonunda uygulamayı başlatın, hedef akışı (örneğin ürün detayına 20 kez girip çıkma) otomasyonla tekrarlayın ve her turun sonunda Mark Generation ekleyin. Son jenerasyondan sonra yaşamaya devam eden ViewModel, Coordinator ve image-cache nesneleri, geçici allocation ile gerçek tutulmuş nesneyi ayırmanızı sağlar.
Karşılaştırmayı aynı cihazda, aynı örnek veri setiyle ve en az üç tekrarla yapın: değişiklikten önce/sonra Persistent Bytes, canlı ViewModel instance sayısı ve Leaks aracındaki cycle sayısını kaydedin. Komut satırından tekrarlanabilir kayıt için bağlı cihaz veya simülatör hedefiyle şu akış kullanılabilir:
xcrun xctrace record --template 'Allocations' --launch -- MyApp --time-limit 45s --output /tmp/catalog-memory.traceBuradaki amaç tek bir “toplam bellek” sayısı değildir; akış bittikten sonra baseline'a dönmeyen sınıf örneklerinin referans zincirini bulmaktır.Deneyimli ekiplerin sık kaçırdığı ayrıntı şudur: SwiftUI navigasyon konteyneri bazı view değerlerini veya state'i kısa süreli yeniden kullanabilir; bu tek başına sızıntı kanıtı değildir. Allocations'ta şüpheli nesneyi seçip Extended Detail → Cycles & Roots ağacında uygulamanın kendi kökünü arayın. Örneğin Task → closure → SearchViewModel zinciri görünüyorsa problem view diffing değil, iptal edilmeyen asenkron işin ViewModel'i tutmasıdır.
iOS MVVM Mimarisi: Task ve ViewModel Retain Cycle'ını Kesme
ios mvvm mimarisi içinde en yaygın hata, ViewModel'in sakladığı Task nesnesinin closure üzerinden aynı ViewModel'i güçlü biçimde yakalamasıdır. Task tamamlanana kadar closure yaşar; closure içindeki güçlü self de ViewModel'i yaşatır. Özellikle sonsuz stream, uzun polling veya askıda kalan HTTP isteğinde bu ilişki ekran kapandıktan sonra da devam eder.
Aşağıdaki desen, sonucu ana aktörde yayınlarken ViewModel'i ağ isteği boyunca güçlü tutmaz. Ayrıca yeni arama başladığında önceki işi iptal eder; yalnızca [weak self] eklemek, eski task'ı iptal etmediği için tek başına yeterli değildir.
@MainActor
final class SearchViewModel: ObservableObject {
@Published private(set) var items: [Product] = []
private var searchTask: Task<Void, Never>?
private let service: ProductService
init(service: ProductService) {
self.service = service
}
func search(_ query: String) {
searchTask?.cancel()
let service = service
searchTask = Task { [weak self] in
do {
let result = try await service.search(query: query)
try Task.checkCancellation()
self?.items = result
} catch is CancellationError {
// İptal, kullanıcı hatası veya hata ekranı değildir.
} catch {
guard !Task.isCancelled else { return }
self?.items = []
}
}
}
deinit { searchTask?.cancel() }
}View tarafında arama metnini .task(id: query) ile bağlamak, view kimliği veya sorgu değiştiğinde SwiftUI'nin önceki işi iptal etmesine yardım eder. Ancak servis katmanında URLSession dışı bir SDK kullanılıyorsa iptalin gerçekten iletildiğini doğrulayın; bazı callback tabanlı SDK'larda Task.cancel() yalnızca Swift concurrency task'ını iptal eder, alttaki isteği etmez. Bu durumda SDK'nın cancel(requestID:) benzeri API'sini withTaskCancellationHandler içinde çağırın.
.task(id: query) {
guard query.count >= 3 else { return }
viewModel.search(query)
}Swift Eğitimi: Closure, Notification ve UIKit Köprülerinde Sahiplik
Bir swift eğitimi kapsamında yalnızca [weak self] ezberlemek yerine sahiplik grafiğini çıkarın. NotificationCenter.addObserver(forName:object:queue:using:) closure tabanlı API'si observer token'ını döndürür; token ViewModel'de tutulurken closure ViewModel'i güçlü yakalarsa cycle oluşur. Token'ı iptal edip nil yapmak ve closure'da zayıf yakalama kullanmak birlikte gereklidir.
final class CartViewModel: ObservableObject {
private var priceObserver: NSObjectProtocol?
init(center: NotificationCenter = .default) {
priceObserver = center.addObserver(
forName: .pricesDidRefresh,
object: nil,
queue: .main
) { [weak self] notification in
self?.reloadPrices(notification)
}
}
deinit {
if let priceObserver {
NotificationCenter.default.removeObserver(priceObserver)
}
}
}xcode eğitimi pratiğinde Memory Graph Debugger'ı tetikleyip CartViewModel için incoming reference'ları açın: __NSObserver veya closure kutusu kök olarak görünüyorsa yukarıdaki bağlantıyı doğrudan doğrularsınız. Combine tarafında da self → Set<AnyCancellable> → sink closure → self halkası oluşabilir; sink { [weak self] value in self?.apply(value) } kullanın. Buna karşılık kısa ömürlü, tek seferlik bir closure'da zayıf yakalama zorunlu değildir; karar nesnenin closure'dan daha uzun yaşayıp yaşamamasına göre verilmelidir.
SwiftUI ile UIViewControllerRepresentable köprüsünde Coordinator'ın sahibi farklıdır: representable struct geçicidir, fakat UIKit delegate'i Coordinator'ı tutabilir. Coordinator içinde view model veya controller referansı gerekiyorsa weak tanımlayın; delegate callback'i ile UIKit controller arasında çift yönlü güçlü referans kurmayın. Bu ayrıntı, yalnızca SwiftUI view değerlerine bakarak görünmeyen ve özellikle kamera, harita veya web view ekranlarında ortaya çıkan bir sızıntı kaynağıdır.
iPhone Uygulama Geliştirme Sürecinde Regresyon Testi ve Yayın Kontrolü
iphone uygulama geliştirme iş akışında sızıntı düzeltmesini manuel test notu olarak bırakmayın. ViewModel'in iptal davranışını async XCTest ile koruyun: test doubles, isteği beklemede tutmalı; stop() çağrısından sonra task iptal edilmeli ve ViewModel serbest kalmalıdır. Aşağıdaki test, asıl üretim kodunda stop() metodunun searchTask?.cancel(); searchTask = nil uyguladığını varsayar.
@MainActor
func testStopReleasesViewModelWithPendingRequest() async {
let service = SuspendedProductService()
weak var weakModel: SearchViewModel?
do {
let model = SearchViewModel(service: service)
weakModel = model
model.search("keyboard")
await service.waitUntilRequestStarts()
model.stop()
}
await Task.yield()
XCTAssertNil(weakModel)
}Bu testin kritik noktası, bekleyen isteğin gerçekten başlamasını beklemektir; aksi halde test task oluşturulmadan geçtiği için yanlış güven verir.Bir ios kursu veya ekip içi kalite kapısında CI'a iki kontrol ekleyin: test şemasında Diagnostics altından Malloc Stack Logging yalnızca teşhis koşularında açın, release adayında ise gerçek cihazda Instruments Leaks kaydı alın. Simulator ile cihaz sonuçlarını aynı kabul etmeyin; bellek basıncı, görsel decode davranışı ve sistem framework cache'leri farklı olabilir. Önce-sonra trace'lerde aynı kullanıcı akışı sonunda kalıcı ViewModel sayısı sıfıra dönüyor, persistent byte eğrisi her turda artmıyorsa düzeltmeyi ölçülebilir biçimde doğrulamış olursunuz.
app store yayınlama öncesinde bu denetimi özellikle giriş/çıkış, satın alma, kamera ve harita gibi uzun yaşayan SDK ekranlarında çalıştırın. Leaks sıfır olsa bile sürekli yükselen persistent allocation, doğrudan cycle olmadan sınırsız uygulama cache'i veya iptal edilmeyen stream anlamına gelebilir. Cache için NSCache kullanıp totalCostLimit belirlemek, sınırsız Dictionary tutmaktan farklı olarak bellek basıncında sistemin nesneleri atabilmesine izin verir.
Swift Kursu İçin İnceleme Listesi: Yanlış Pozitifleri Ayıklamak
Bir swift kursu kod incelemesinde her deinit logunu kanıt saymayın: optimize edilmiş derlemelerde zamanlama değişebilir ve framework cache'leri nesneleri beklenenden uzun tutabilir. Bunun yerine Allocations'ta class instance sayısını akış başlangıcı ve sonundaki generation'lar arasında karşılaştırın, ardından Memory Graph'ta uygulama kaynaklı güçlü yol olup olmadığını bulun. URLCache, image decoder ve CoreText kaynaklı sistem köklerini uygulama retain cycle'ı diye “düzeltmeye” çalışmak, cache hit oranını bozup sorunu çözmez.
Kod incelemesinde şu üç aramayı otomatikleştirin: grep -R "sink {" ile Combine closure'ları, grep -R "Task {" ile sahipliği belirsiz task'ları ve grep -R "addObserver" ile token tabanlı observer'ları listeleyin. Her sonuç için bir sahip, bir iptal noktası ve Instruments'ta doğrulanabilir bir kapanış akışı tanımlanmalıdır; bu üçlü yoksa bellek sorunu genellikle ilk uzun kullanıcı oturumunda görünür.
İlgili Eğitim
YTÜSEM İlgili Eğitim
Sık Sorulan Sorular
SwiftUI eğitimi sırasında Instruments ile bellek sızıntısı nasıl bulunur?
Allocations şablonunda hedef ekran akışından önce ve sonra Mark Generation ekleyin. Son generation'da kalan ViewModel'i seçip Cycles & Roots ile onu tutan Task, closure veya observer token zincirini inceleyin; ardından aynı akışı düzeltme öncesi ve sonrası üç kez çalıştırın.
iOS MVVM mimarisinde Task ViewModel'i neden serbest bırakmaz?
ViewModel bir Task'ı property olarak tutarken Task closure'ı self'i güçlü yakalıyorsa döngü oluşur. Önceki Task'ı yeni işlemde iptal edin, sonuç atamasında weak self kullanın ve ekranın yaşam döngüsünde stop/cancel noktası tanımlayın. Uzun yaşayan SDK isteklerinde Task iptalinin SDK iptal API'sine aktarıldığını ayrıca doğrulayın.
Xcode eğitimi için Memory Graph ile Leaks arasındaki fark nedir?
Leaks, genellikle erişilemeyen ancak retain cycle nedeniyle serbest bırakılamayan blokları bulur. Memory Graph ise erişilebilir durumda kalan bir ViewModel'in onu tutan referanslarını gösterir. Bu nedenle ekran kapandıktan sonra hâlâ erişilebilir olan yanlış sahiplikleri teşhis etmek için ikisini aynı senaryoda kullanın.
App Store yayınlama öncesi iPhone uygulama geliştirme bellek testi nasıl yapılmalı?
Gerçek cihazda giriş-çıkış, medya seçimi ve uzun liste dolaşımı gibi akışları Instruments Allocations ve Leaks ile kaydedin. Her akış sonunda persistent instance eğrisinin artmadığını, ViewModel sayısının baseline'a döndüğünü ve uygulama kökenli retain path kalmadığını kontrol edin; simulator sonucunu tek başına yayın kararı için kullanmayın.
AI / LLM Discovery
Bu makale Opendart Akademi iOS / Swift eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.


