Sobes.tech
Middle

Swift'te Otomatik Bellek Yönetimi (ARC) nasıl çalışır?

sobes.tech yapay zeka

AI'dan gelen yanıt

Swift'te Otomatik Referans Sayacı (ARC), uygulamalarınızın bellek kullanımını izler ve yönetir. Artık ihtiyaç duyulmayan sınıf örnekleri tarafından kullanılan belleği otomatik olarak serbest bırakır, böylece bellek sızıntılarını önler.

Yeni bir sınıf örneği oluşturulduğunda, ARC bu örnek hakkında bilgi saklamak için bir bellek bloğu ayırır. Artık gerekmediğinde, ARC bu belleği serbest bırakır ve başka amaçlar için kullanılabilir hale getirir.

ARC, her sınıf örneğine güçlü referansların (strong references) sayısını sayarak çalışır. Güçlü bir referans, örneği bellekte tutar. Güçlü referansların sayısı sıfıra düştüğünde, ARC ilgili belleği serbest bırakır.

Swift'te ARC'yi etkileyen üç referans türü vardır:

  1. Güçlü Referanslar (Strong References): Referans sayacını artırır.
  2. Zayıf Referanslar (Weak References): Referans sayacını artırmaz. Güçlü referans döngülerini önlemek için kullanılır, iki sınıf örneği birbirine güçlü referanslar tutarken. Zayıf referans otomatik olarak nil olur örnek yok edildiğinde.
  3. Unowned Referanslar (Unowned References): Ayrıca referans sayacını artırmaz. Bir örneğin, diğer örnek var olduğu sürece var olacağından emin olduğunuz durumlar için kullanılır. Opsiyonel olamaz (nil). Bu referansa erişmeye çalışmak çalışma zamanında hata oluşturur.

Güçlü referans döngüsüne örnek:

class Person {
    let name: String
    var apartment: Apartment?

    init(name: String) {
        self.name = name
        print("\(name) başlatılıyor")
    }

    deinit {
        print("\(name) sonlandırılıyor")
    }
}

class Apartment {
    let unit: String
    var tenant: Person?

    init(unit: String) {
        self.unit = unit
        print("Daire \(unit) başlatılıyor")
    }

    deinit {
        print("Daire \(unit) sonlandırılıyor")
    }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john

john = nil // Person sonlandırılmıyor
unit4A = nil // Apartment sonlandırılmıyor

Bu döngüyü çözmek için, zayıf veya unowned referanslar kullanın. Bu durumda, Apartment içindeki tenant zayıf bir referans olmalı:

class Person {
    let name: String
    var apartment: Apartment?

    init(name: String) {
        self.name = name
        print("\(name) başlatılıyor")
    }

    deinit {
        print("\(name) sonlandırılıyor")
    }
}

class Apartment {
    let unit: String
    weak var tenant: Person? // Zayıf referans kullanımı

    init(unit: String) {
        self.unit = unit
        print("Daire \(unit) başlatılıyor")
    }

    deinit {
        print("Daire \(unit) sonlandırılıyor")
    }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john?.apartment = unit4A
unit4A?.tenant = john

john = nil // Person sonlandırılıyor
unit4A = nil // Apartment sonlandırılıyor

Nesneler arasındaki ilişkiye bağlı olarak, zayıf veya unowned referanslar arasında seçim yapın:

  • Zayıf (Weak): Bir nesne, diğer nesne yok edilmeden önce nil olabilir (opsiyonel değer).
  • Unowned: Bir nesne, diğer nesne var olduğu sürece var olur (opsiyonel olmayan değer).

ARC ayrıca, sınıf örneklerini yakalayan kapanımların (closures) kullandığı belleği de yönetir. Güçlü referans döngüleri, bir kapanımın sınıf örneğini yakalaması ve bu örneğin de kapanıma güçlü referansla bağlı olması durumunda oluşabilir. Bunu önlemek için, kapanımlarda yakalama listeleri (capture lists) kullanılır.

class HTMLElement {
    let name: String
    let text: String?

    lazy var asHTML: () -> String = {
        // Capture list yok, güçlü döngü olası
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name)>"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) sonlandırılıyor")
    }
}

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "merhaba, dünya")
print(paragraph!.asHTML())

paragraph = nil // HTMLElement sonlandırılmıyor

Çözüm, [weak self] kullanımı:

class AnHTMLElement {
    let name: String
    let text: String?

    lazy var asHTML: () -> String = { [weak self] in // weak self kullanımı
        guard let self = self else { return "" } // self’in var olup olmadığını kontrol et
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name)>"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) sonlandırılıyor")
    }
}

var paragraph: AnHTMLElement? = AnHTMLElement(name: "p", text: "merhaba, dünya")
print(paragraph!.asHTML())

paragraph = nil // AnHTMLElement sonlandırılıyor

[unowned self] kullanımı, eğer sınıf örneğinin (self) kapanmadan önce nil olmayacağından eminseniz, mümkündür.

ARC, manuel yönetimle karşılaştırıldığında Swift'te bellek yönetimini büyük ölçüde kolaylaştırır, ancak sızıntıları önlemek için referans türlerinin anlaşılmasını gerektirir.