Her iOS geliştirici bir noktada bellek yönetimiyle saç baş yolmuştur. Ben de bugün size kurtarıcım olan bir yapıdan bahsedeceğim: Autoreleasepool.

Autoreleasepool Nedir?

Düşünün ki büyük bir temizlik yapıyorsunuz ve bir sürü çöp poşeti kullanıyorsunuz. Her poşeti doldurup bekletmek yerine, poşetler dolar dolmaz çöpe atarsanız eviniz daha düzenli kalır, değil mi? İşte autoreleasepool da tam olarak böyle çalışır. Uygulamamızda oluşturduğumuz geçici nesneleri hemen temizlememize olanak sağlayan bir mekanizmadır.

Teknik olarak bakacak olursak, autoreleasepool aslında bir closure bloğu olarak çalışır ve içerisinde oluşturulan nesnelerin bellek yönetimini üstlenir. ARC (Automatic Reference Counting) normalde bu nesneleri scope sonunda temizlerken, autoreleasepool sayesinde blok içindeki nesneleri anında temizleyebiliyoruz. Özellikle döngüler içinde çok sayıda geçici nesne oluşturduğumuzda (örneğin bir dizi resmi işlerken), bu mekanizma bellek kullanımını optimize etmemize yardımcı oluyor.

Autoreleasepool’a Neden İhtiyaç Duyarız?

Biliyorsunuz Swift’te ARC (Automatic Reference Counting) sayesinde bellek yönetimi büyük oranda otomatik yapılıyor. “E o zaman neden autoreleasepool’a ihtiyacımız var?” diye düşünebilirsiniz. Haklısınız, çoğu durumda ARC işimizi görüyor. Ama öyle senaryolar var ki, ARC tek başına yeterli olmuyor.

Mesela büyük bir döngü düşünün. Bu döngüde her adımda yeni görseller oluşturuyorsunuz. ARC bu görselleri hemen silmiyor, döngü bitene kadar bekliyor. Eğer döngünüz milyonlarca kez dönüyorsa? İşte o zaman belleğiniz şişmeye başlıyor. Autoreleasepool tam da burada imdadımıza yetişiyor. Her döngü adımında oluşturduğumuz nesneleri hemen temizleyebiliyoruz.

Autoreleasepool Kullanım Alanları

Ben genellikle autoreleasepool’u şu durumlarda kullanıyorum:

  1. Döngülerle çalışırken: Özellikle binlerce öğeyi işlediğim döngülerde, her iterasyonda oluşan geçici nesnelerin hemen temizlenmesini istiyorum.
  2. Görüntülerle çalışırken: Yüksek çözünürlüklü görselleri işlerken, her görsel işleminden sonra belleği hemen temizlemek istiyorum.

Autoreleasepool’u Deneyelim

Elimizde iki fonksiyon var. İkisi de aynı işi yapıyor: 100 kere döngü dönüyor ve her seferinde büyük bir görüntü oluşturuyor. Tek farkları, birinde Autoreleasepool kullanıyoruz, diğerinde kullanmıyoruz.

Autoreleasepool kullanmadığımız durum:

func withoutAutoreleasepool() {
    for _ in 0..<100 {
        let image = UIImage(data: generateLargeImageData())
        _ = image
    }
}

Bu fonksiyonda her döngüde yeni bir görüntü oluşturuyoruz, ancak bu görüntüler döngü tamamlanana kadar bellekte tutuluyor. Yani 100 tane büyük görüntü aynı anda bellekte duruyor! İşte tam da bu yüzden RAM kullanımı her iterasyonda artıyor ve sonunda 4,43GB gibi devasa bir boyuta ulaşıyor. Düşük RAM’li bir cihazda bu kodu çalıştırsaydık, uygulamamız büyük ihtimalle çökerdi.

Autoreleasepool kullandığımız durum:

func withAutoreleasepool() {
    for _ in 0..<100 {
        autoreleasepool {
            let image = UIImage(data: generateLargeImageData())
            _ = image
        }
    }
}

Şimdi aynı işlemi Autoreleasepool kullanarak yapıyoruz. Her iterasyonda oluşturduğumuz görüntü, o iterasyon tamamlandığında hemen bellekten siliniyor. Yani bir sonraki iterasyona geçmeden önce bellek temizleniyor. RAM kullanımı maksimum 69,9MB’da kalıyor! Üstelik iterasyonlar arasında bu değer daha da düşük seviyelere iniyor.

Sonuç

Örnekte gördüğümüz gibi, autoreleasepool kullanmadığımızda bellek kullanımı her iterasyonda artarak devam ediyor ve kontrolden çıkıyor. Ancak autoreleasepool kullandığımızda, bellek kullanımı dengeli ve kontrollü bir şekilde ilerliyor. Bu da uygulamamızın daha stabil çalışmasını ve kullanıcı deneyiminin olumsuz etkilenmemesini sağlıyor. Özellikle döngüler içinde yoğun bellek kullanımı gerektiren işlemler yapıyorsanız, autoreleasepool sizin kurtarıcınız olabilir.

NOT: Örnekteki kodun tamamına buradan erişebilirsiniz.