Swift'te Referans Sayımı - strong, weak, unowned



Bu yazıda, Apple’ın Swift’deki bellek yönetiminin nasıl olduğunu inceleyeceğiz. Çoğunlukla otomatik olarak ele alınsa bile, hala bazı önemli noktalar vardır. Nesneler arasındaki ilişkiyi tanımlamak için doğru referans tipini seçmek, hafıza sızıntılarını önlememize yardımcı olur.
Genellikle bu konu iş mülakatlarında soru olarak gelmektedir.

Otomatik Referans Sayacı

Apple’ın bellek kullanımının otomatik olarak izlenmesine ve yönetilmesine yönelik uygulamasına ARC (Otomatik Referans Sayacı - Automatic Reference Counting) adı verilir. Nesneler artık referans alınmadığında belleği otomatik olarak boşaltır. Hangi nesnelerin hala kullanımda olduğunu bilmek için referans sayısını arttırıp azaltarak nesneler arasındaki ilişkiyi izler. Referans sayısı sıfır olduğunda nesneler serbest bırakılabilir. ARC yalnızca Sınıf(Class) örnekleri için geçerlidir. Yapılar, Enums'ler ve Değer Türleri referans tipte değildir. Sınıflar yerine Yapıları kullanmak bu konuda daha avantajlı bir nedendir diyebiliriz.

Strong 

Swift'te neredeyse her yerde strong referanslar bulacaksınız çünkü bu bir nesnenin varsayılanıdır. Doğrusal bir referans akışınız olduğunda bu bir soruna yol açmaz. Eğer ebeveyni serbest bıraktığınızda ve referans sayısını azalttığınızda tüm çocuklar da azalır. İşte strong bir referans örneği:
import Foundation
class Car {
    var brand: String
init(brand: String) {
        self.brand = brand
        print("Car of the brand \(brand) allocated")
    }
deinit {
        print("Car of the brand \(brand) is being deallocated")
    }
}
do {
    let tesla = Car(brand: "tesla")
}

Araba nesnesinin başlangıcı do bloğundadır. Bu onun etrafında bir alan yaratır. Nesne do bloğundan dolayı serbest bırakılır ve her şey yolunda girer:

//Car of the brand tesla allocated
//Car of the brand tesla is being deallocated

Ancak, sınıf örnekleri arasında strong bir referans döngüsü olan bir kod yazmak mümkündür. Önceki örneğin bu değiştirilmiş versiyonu bir referans döngüsü gösterir:
import Foundation
class Car {
    var brand: String
    var owner: Owner?
 
    init(brand: String) {
       self.brand = brand
       print("Car of the brand \(brand) allocated")
    }
 
    deinit {
        print("Car of the brand \(brand) is being deallocated")
    }
}
class Owner {
    var name: String
    var car: Car?
 
    init(name: String) {
        self.name = name
        print("Owner \(name) allocated")
    }
 
    deinit {
        print("Owner \(name) deallocated")
    }
}
do {
    let tesla = Car(brand: "tesla")
    let misterX = Owner(name: "Mister X")
    tesla.owner = misterX
    misterX.car = tesla
}

Araç nesnesinin artık bir Owner'a bağlı bir referansı vardır ve Owner'ın bir Otomobil için isteğe bağlı bir referansı vardır. Çıktı:

//Car of the brand tesla allocated
//Owner Mister X allocated

Güçlü referans döngüsü, ayrılma ve hafızayı boşaltmak için referans sayısının sıfıra ulaşmasını önler. Bu sorunu çözmek için weak referanslara ihtiyacımız var.

Weak

Güçlü referansların aksine weak, referans sayısını bir artırmaz. Böylece nesneyi ARC tarafından tahsis edilmekten koruyamazlar. Ayrılma durumunda zayıf referans otomatik olarak sıfır olarak ayarlanır. Tüm zayıf referansların isteğe bağlı değişkenleri değiştirmesinin nedeni budur. Bir sabiti zayıf olarak tanımlamak mümkün değildir.
Önceki örneğe bir göz atalım. Otomobille araç sahibi arasında zayıf bir referans kullanmak, yerinden çıkarma işleminin düzeltilmesine yardımcı olur:
class Car {
    weak var owner: Owner?
    ...
}
class Owner {
    weak var car: Car?
    ... 
}
...

Şimdi ARC, tüm nesneleri doğru şekilde dağıtıyor:

//Car of the brand tesla allocated
//Owner Mister X allocated
//Owner Mister X deallocated
//Car of the brand tesla is being deallocated

Not: Her ikisi yerine Araba veya Sahip sınıfında yalnızca bir zayıf referans kullanılması sorunu da çözmüş ve güçlü referans döngüsünü çözmüştür.

Sahipsiz referanslar, zayıf olanlara benzer şekilde davranır. Tutulma sayısını da bir artırmazlar.Zayıf referanslardan farklı olarak, sahipsiz referansların bir İsteğe Bağlı Olması gerekmez; Sadece istenmeyen referansları kullanmanız, nesnenin ayarlandıktan sonra asla sıfır olmayacağını gerçekten bildiğiniz zaman önemlidir. 
Apple’a göre, referansınızla referans kodun aynı anda kaldırılacağı zaman kullanmak en iyisidir.

Weak vs Unowned - Apple'dan bir öneri

"Bu referansın kullanım ömrü boyunca bir noktada sıfır olması geçerli olduğunda zayıf bir referans kullanın. Bunun tersine, referansın başlangıç ​​sırasında ayarlandıktan sonra hiçbir zaman sıfır olmayacağını bildiğiniz zaman, Unowned bir referans kullanın." 
Apple Documentation