defer, Go'da kaynak yönetimini (örneğin dosyalar, mutex, bağlantılar) basitleştirmek için geliştirilmiştir - bir fonksiyonun sonunda işlemlerin gerçekleştirilmesini garanti etmek gereken herhangi bir durumda. Tarihsel olarak diğer dillerde (Java'da finally, try-with-resources) benzer yapılar mevcuttu ancak Go, daha açık ve anlaşılır bir model sunar.
Sorun: Kaynakların her zaman serbest bırakıldığından emin olmak gerekir, hata çıkarsa veya panic olursa bile. Kaynağın iki kez kapatılması veya sızıntı, klasik programlama stilinde yaygın bir sorundur.
Çözüm: Fonksiyon veya metod içinde defer ile tanımlanan her şey, çağrı yığınına yerleştirilir ve fonksiyondan çıkmadan önce ters sırayla yürütülür. Bu, kaynakların, istisnalar (panic) veya erken return durumlarında bile serbest bırakılmasını garanti eder.
Kod örneği:
func processFile() error { f, err := os.Open("filename.txt") if err != nil { return err } defer f.Close() // dosya kapatılması en sona kalacak // dosya ile çalışma return nil }
Temel özellikler:
Panic oluştuğunda defer'ler çalışacak mı?
Evet! Panic durumunda bile tüm defer işlevleri çağrılacak, bu "finalizasyon"un ana mekanizmasıdır.
Defer'e geçirilen işlevin argümanları ne zaman hesaplanır?
Defer tanımlandığında, gerçekten yürütülürken değil. Bu nedenle, daha sonra değiştirilecek değişkenler kullanılıyorsa, bunu dikkate almak önemlidir:
а := 1 defer fmt.Println(a) a = 2 // 1 yazacak, 2 değil
Defer döngü içinde nasıl çalışır? Bu bellek sızıntısına yol açar mı?
Eğer her döngü iterasyonunda defer kullanılıyorsa, tüm defer'ler yalnızca tüm fonksiyon tamamlandıktan sonra çalıştırılacaktır, her iterasyondan sonra değil - tüm defer işlevlerinin yığını birikir ve bu fazla bellek tüketimine yol açabilir.
for i := 0; i < 3; i++ { defer fmt.Println(i) }
Bir döngüde binlerce dosya açılıyor ve her biri için defer kullanılıyor. Tüm dosyalar yalnızca fonksiyonun sonunda kapatılacak ve kaynaklar gecikecek, bu da "sızıntıya" - açık dosya limitini aşmaya neden olacak.
Artılar:
Eksiler:
Döngüde yerel işlevler kullanılıyor, burada defer yalnızca bu dosyanın kapsamı için uygulanıyor, tüm işleyici için değil:
for _, name := range fileNames { func() { f, _ := os.Open(name) defer f.Close() // f ile çalışma }() }
Artılar:
Eksiler: