ProgramlamaiOS/Mobil Geliştirici

Swift'te closures (kapsamlar) nasıl çalışır, fonksiyonlardan ne farkları vardır ve bunların kullanımıyla ilgili hangi bellek yönetimi yönleri vardır?

Hintsage yapay zeka asistanı ile mülakatları geçin

Cevap.

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:

  • kapsam değerleri referansla veya değerle yakalayabilir
  • escaping/non-escaping kapanımlar, kapsamın yaşam döngüsünü etkiler
  • saklanan özellikler olarak kapanımlar, kolayca retain cycle oluşturur

Kandırmaca sorular.

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

Yaygın hatalar ve anti-paterni

  • Kapsamı sınıfın özelliği olarak ayarlamak ve [weak self] olmadan self'e erişmek.
  • Kapsamı çok büyük tanımlamak - bu, anlayışı ve hata ayıklamayı zorlaştırır.
  • Escaping kullanmak, eğer non-escaping ile geçiştirilebiliyorsa.

Gerçek hayattan bir örnek

Olumsuz vaka

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.

Olumlu vaka

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.