Sorunun geçmişi:
Kapsamlar, fonksiyonel dillerden gelen bir kavramdır ve kod bloklarını değerler olarak iletmeyi sağlar. Swift'te closures (diğer dillerdeki lambda benzerleri) başlangıçtan beri vardır. Bu sayede asenkron çağrılar, eylem devretme, koleksiyonları sıralama, filtreleme vb. gibi işlemleri şık bir şekilde gerçekleştirmek mümkündür.
Sorun:
Kapsamlar çevrelerindeki değişkenleri yakalar. Bu değişkenler sınıf nesnelerine referans içeriyorsa, özellikle kapsama sınıfının bir özelliği olarak kaydediliyorsa, retain cycle (tutma döngüsü) oluşabilir. Ayrıca escaping ve non-escaping kapanımlar arasındaki farkı anlamak ve değer yakalama şeklini bilmek önemlidir.
Çözüm:
Swift, kapsamları bağımsız nesneler olarak uygular, bunlar bağlamı yakalayabilir ve kapsamın sahibi mimariye dikkat etmelidir.
Kod örneği:
class Performer { var onComplete: (() -> Void)? func doWork() { onComplete = { print("İş tamamlandı!") } } } // Kapsam içinde self'in yakalanması class Test { var value = 0 func start() { DispatchQueue.global().async { [weak self] in self?.value = 1 } } }
Anahtar özellikler:
Eğer kapsam self'i açıkça yakalamıyorsa, retain cycle oluşabilir mi?
Evet, eğer kapsam sınıfın güçlü bir özelliği olarak kaydediliyorsa ve içindeki self'e erişiyorsa, self'i açıkça yazmasanız bile retain cycle oluşur. [weak self]/[unowned self] kullanın veya mümkünse kapsamı yerel yapın.
Escaping ile non-escaping kapanımlar arasındaki fark nedir?
Escaping kapanım, fonksiyonun tamamlanmasından sonra çağrılabilir ve genellikle asenkron işlemler için kullanılır. Non-escaping, fonksiyonun tamamlanmasından önce işlenir. Escaping'in değer yakalama sırası farklıdır.
Kod örneği:
func asyncWork(completion: @escaping () -> Void) { DispatchQueue.global().async { completion() } }
Kapsam, yakalanan değişkenlerin değerlerini değiştirebilir mi?
Evet, eğer kapsam bir var olarak tanımlanmış bir değişkeni yakaladıysa, değerini değiştirebilir (değer türleri için). Sınıf için bu bir referans olacak ve her zaman özellikleri değiştirebilirsiniz.
Kod örneği:
var value = 1 let closure = { value += 1 } closure() print(value) // 2
ViewController, kapsamı bir özellik (örneğin, completionHandler) olarak saklar ve içinde doğrudan self'e erişir. Sonuç olarak bir retain cycle oluşur: ViewController => kapsam => ViewController. Ekranın kapatılması bellek tahsisini gerçekleştirmez.
Artılar: Kod derli toplu ve kısa görünüyor.
Eksiler: Bellek sızıntıları, ViewController bellekte "takılı kalır", potansiyel hatalar.
Kapsam içinde [weak self] veya [unowned self] kullanılır veya kapsam, nesnenin yaşam döngüsünden daha uzun süre saklanmaz. Benzer yerlerin kod incelemesi sırasında incelenmesi gerekmektedir.
Artılar: Kaynakların düzgün serbest bırakılması, öngörülemeyen sızıntıların olmaması.
Eksiler: [weak self] kullanımı, boş göstermek konusunda dikkat gerektirir ve yanlış kullanımda ani çökme meydana gelebilir.