Swift'te closure'lar (kapamalar) çevresel bağlamdan değerleri ve referansları "kapma" yeteneğine sahiptir. Eğer kaplanan değer bir değer tipi (struct, enum) ise, closure verileri kopyalar. Eğer bir referans (örneğin, sınıf) kaplanıyorsa, closure sınıfın örneğine bir referans saklar, bu da [weak self] veya [unowned self] kullanılmadığında retain cycle'a yol açabilir.
Capture mekanizması, asenkron görevler ve callback'ler uygulamayı sağlar, ancak yanlış kullanım bellek yönetimini zorlaştırır.
Örnek:
class MyClass { var value = 10 func doSomething() { let closure = { [weak self] in print(self?.value ?? "nil") } closure() } }
Bu örnekte, asenkron çalışma yürütüldüğü için güçlü referans döngüsünden kaçınmak amacıyla [weak self] kullanılmıştır.
Soru: "Her zaman [weak self] kullanımı, closure içinde self yakalandığında bellek sızıntısını garanti eder mi?"
Cevap: Hayır, eğer closure içinde güçlü bir referans oluşursa (örneğin, ara değişkenler üzerinden), [weak self] ile bile retain cycle elde edilebilir. Örneğin:
let closure = { [weak self] in let strongSelf = self strongSelf?.doSomething() }
Eğer closure'ı uzun ömürlü bir nesneye (örneğin, NotificationCenter) bağlarsanız, self için başka referanslar varsa retain cycle elde edebilirsiniz.
Hikaye 1
Bir iOS uygulama projesinde, bir ağ isteğine closure gönderirken
[weak self]belirtmeyi unutan bir geliştirici oldu. Sonuç olarak, istek gerçekleştirilirken, kullanıcı ekranı kapasa bile view controller nesnesi serbest bırakılmadı. Bu, ağ işlemleri yoğun bir şekilde kullanıldığında bellek sızıntılarına yol açtı.
Hikaye 2
Zamanlayıcıya abonelik yapan karmaşık bir sistemde,
weakkullanılmadan closure üzerinden abone olan bir nesne, bellekten ekran silindiğinde bile eylemlerine devam etti. Bu, artık hiyerarşide olmayan UI elemanlarının tekrar çağrılmasına yol açtı ve uygulamanın çökmesine neden oldu.
Hikaye 3
Veri önbellekleme uygulaması sırasında closure, uzun ömürlü önbellek nesnesinde self'in referansını
[unowned self]anotasyonu olmadan yakaladı. Orijinal nesne yok edildikten sonra closure'dan self'e erişim, zaten serbest bırakılmış belleğe erişim gerektiriyordu ve uygulamanın çökmesine neden oldu.