Apple tarafından geliştirilen Swift; esnekliği, güvenliği ve modernliği ile geliştiricilere birçok kolaylık sağlıyor. Birçok yerde ve şekilde kullandığımız veri modelleri bunların başında geliyor. Fakat bazen Struct mı yoksa Class mı kullanayım sorusu kafa karıştırıcı olabiliyor. İşte tam bu noktada bu iki veri tipi arasındaki farka ve nerelerde kullanabileceğimize gelin birlikte bakalım.
Class Nedir?
Swift içerisinde farklı veri tiplerini bir arada tutabilmenizi sağlayan bir veri yapısıdır. Referans tiplidir(reference type). Yani bir ‘Class’ nesnesi başka bir değişkene atandığında veya bir fonksiyona parametre olarak geçirildiğinde referansı kopyalanır. Bu yüzden bir ‘Class‘ nesnesini değiştirirseniz, onu referans alan diğer değişkenlerde bu değişimden etkilenecektir. Peki Class nasıl oluşturulur ve kullanılır?
class Car {
var brand : String
var year : Int
var color : String
init(brand : String, year : Int, color : String) {
self.brand = brand
self.year = year
self.color = color
}
}
var myCar = Car(brand: "Porsche", year: 2024, color: "Black")
Struct Nedir?
Struct’ta aslında temel olarak Class gibi farklı veri tiplerini bir arada tutabilmenizi sağlayan bir veri yapısıdır. Fakat değer tiplidir (value type). Yani bir ‘Struct’ nesnesi bir değişkene veya fonksiyona parametre olarak geçirildiğinde kopyalanır. Bu sayede basit veri modelleri oluşturabilir, yaşam döngüsü içerisinde kolayca yönetebilirsiniz. Peki Struct nasıl oluşturulur ve kullanılır?
struct Car {
var brand : String
var year : Int
var color : String
}
var myCar = Car(brand: "Porsche", year: 2024, color: "Black")
Kısaca Farklarına Bakacak Olursak
Class | Struct |
---|---|
Referans Tip (Reference Type) | Değer Tip (Value Type) |
Kalıtım (Inheritance) kullanılabilir | Kalıtım (Inheritance) kullanılamaz |
Bellek öbek (Heap) olarak ayrılır | Bellek yığın (Stack) olarak ayrılır |
Varsayılan olarak değiştirilebilir (Mutable) | Varsayılan olarak değiştirilmez (Immutable) |
Referans Tip ve Değer Tip
Aslında Struct ve Class veri tiplerini tanımlarken bu konuya biraz değinmiştik. Yakından bakacak olursak eğer;
Struct
- Struct değer tiptir. Yani bir Struct’tan yeni bir örnek oluşturduğunuzda verilerin bir kopyasıyla çalışırsınız.
- Yapacağınız değişiklik sadece üzerinde çalıştığınız örnek için geçerli olur. Diğer örnekleri etkilemez.
- Başka bir değişkene veya fonksiyona parametre olarak aktarıldıklarında kopyalanırlar.
struct Car {
var brand : String
var year : Int
var color : String
}
var myCar = Car(brand: "Porsche", year: 2024, color: "Black")
var mySecondCar = myCar
mySecondCar.color = "Blue"
print(myCar.color) //Black çıktısı alınır
Class
- Class’lar referans tiptir. Yani bir Class’ın örneğini oluşturduğunuzda bellekte örneğe atanmış bir referansla çalışırsınız.
- Oluşturacağınız birden fazla örnek aynı referansı işaret edebilir. Bu yüzden bir örnek için yapacağınız değişiklik diğer örnekler içinde geçerli olur.
- Bir değişkene veya fonksiyona parametre olarak aktarıldıklarında referans yapısı dikkate alınmalıdır.
class Car {
var brand : String
var year : Int
var color : String
init(brand : String, year : Int, color : String) {
self.brand = brand
self.year = year
self.color = color
}
}
var myCar = Car(brand: "Porsche", year: 2024, color: "Black")
var mySecondCar = myCar
mySecondCar.color = "Blue"
print(myCar.color) //Blue çıktısı alınır
Kalıtım
Nesne yönelimli programlamanın temel taşlarından biri olan kalıtım, aslında adından da anlaşılacağı üzere özellik aktarımına yarar. Fakat Swift ile programlama yapıyorsanız bu kavramı yalnızca Class ile kullanabilirsiniz. Oluşturacağınız bir temel sınıfa ek olarak bir veya daha fazla alt sınıf ile hiyerarşiler oluşturmaya olanak tanır. Bu sayede alt sınıflar ana sınıfın özelliklerini ve yöntemlerini miras alabilir. Ayrıca oluşturacağınız bu yapı ile bir diğer nesne yönelimli programlama ilkesi olan çok biçimliliğe (polymorphism) kapı açabilirsiniz. Fakat vurguladığımız gibi bu özellikleri sadece Class ile kullanabilirsiniz. Çünkü oluşturulan her Struct tamamen bağımsızdır. Bu yüzden kalıtımı desteklemez.
Burada dikkat edilmesi gereken bir diğer nokta ise protokollerdir (Protocol). Bir protokolün Class’a ve Struct’a uygulanma şekli kalıtıma çok benzesede bu işlem kalıtım değildir. Burada aslında Class ve Struct, protokol tarafından belirlenen kuralı uygulamak zorunda olduğunu belirtir.
class Car {
var brand : String
var year : Int
var color : String
init(brand : String, year : Int, color : String) {
self.brand = brand
self.year = year
self.color = color
}
}
class Mercedes : Car {
var model : String = ""
func carDetail() {
print("Car Detail : \(year) \(color) \(brand) \(model)")
}
}
var myMercedes = Mercedes(brand: "Mercedes", year: 2024, color: "Black")
myMercedes.model = "CLS"
myMercedes.carDetail() //Car Detail : 2024 Black Mercedes CLS çıktısı alınır
Bellek Yönetimi
Bellek yönetimi Struct ve Class arasında seçim yapmak istediğinizde karşınıza çıkan bir diğer kritik nokta olabilir. Bu iki veri tipinin sahip olduğu yapısal fark, bellek yönetimi konusunda da farklılıklara neden olabiliyor. Yakından bakacak olursak eğer;
Struct
- Struct değer (value) tipli olduğu için bellekte yığın (stack) olarak tutulur. Bu sayede daha küçük veri türleri için yüksek performans sağlanır.
- Struct örnekleri sonlu yaşam döngülerine sahiptir. Yani kullanım sona erdiğinde otomatik olarak bellekten ayrıştırılır.
- Sınırlı yaşam döngüsü ve değer tipi olması nedeniyle Struct ile ARC(Otomatik Referans Sayma) kullanılmaz.
Class
- Class referans (reference) tipli olduğu için bellekte öbek (heap) olarak tutulur. Bu sayede daha büyük ve karmaşık yapıların kurulumuna izin verilir.
- Class örnekleri oluşturuldukları kapsam ötesine uzanan yaşam sürelerine sahiptir. Bu uzun yaşam döngüsü nedeniyle, kontrolünün manuel gerçekleştirilmesi gerekir.
- Manuel referans takibi için Swift içerisinde ARC(Otomatik Referans Sayımı) kullanılır. Bu yapı bir Class örneğinde kaç adet referans olduğunu takip eder ve kullanım durumuna göre bellekten ayrıştırılmasını sağlar. Bu sayede bellek yönetimi sağlanmış olur.
Değiştirilebilirlik
Bir diğer kritik tercih nedenlerinden biri olan değiştirilebilirlik, Struct ve Class veri tiplerinin yine birbirinden ayrışmasına neden oluyor. İncelemek istersek eğer;
Struct
- Struct özellikleri ‘let’ ile bildirildiklerinde varsayılan olarak sabit olur ve değiştirilemezler. Ancak ‘var’ anahtar kelimesi ile belirlenen özellikler değiştirilebilir.
- Bu durum oluşturduğunuz veri modeli için güvenlik ve değişmezlik sağlar.
struct Car {
var brand : String
var year : Int
var color : String
}
let myCar = Car(brand: "Porsche", year: 2024, color: "Black")
var myCar2 = Car(brand: "Mercedes", year: 2024, color: "Red")
myCar.brand = "Mercedes" // {Cannot assign to property: 'myCar' is a 'let' constant} Hatası alınır
myCar2.year = 2023 // Hata alınmaz
Class
- Class özellikleri, ‘let’ ve ‘var’ anahtar kelimelerine bakılmaksızın değiştirilebilir.
- Bu esneklik Class örneğinde daha kolay değişiklik yapılabilmesini sağlar.
class Car {
var brand : String
var year : Int
var color : String
init(brand : String, year : Int, color : String) {
self.brand = brand
self.year = year
self.color = color
}
}
let myCar = Car(brand: "Porsche", year: 2024, color: "Black")
var myCar2 = Car(brand: "Mercedes", year: 2024, color: "Red")
myCar.brand = "Mercedes"
myCar2.year = 2023
print(myCar.brand) // {Mercedes} yazdırılır
print(myCar2.year) // {2023} yazdırılır
Ayrıca oluşturduğunuz veri modeli içinde yer alan bir özelliği bir metot ile değiştirmek istediğinizde Class ile kolayca yapabiliyorken Struct ile yapamazsınız. Struct ile çalışıyor ve içerisindeki bir özelliği değiştirmek istiyorsanız, yazacağınız metot ile birlikte ‘mutating’ anahtar kelimesini kullanmalısınız.
class Car {
var brand : String
var year : Int
var color : String
init(brand : String, year : Int, color : String) {
self.brand = brand
self.year = year
self.color = color
}
func changeYear (newYear : Int) {
self.year = newYear
print("Updated Car Details : \(year) \(color) \(brand)")
}
}
let myCar = Car(brand: "Porsche", year: 2024, color: "Black")
myCar.changeYear(newYear: 2023)
//{Updated Car Details : 2023 Black Porsche} yazdırılır
struct Car {
var brand : String
var year : Int
var color : String
mutating func changeYear (newYear : Int) {
self.year = newYear
print("Updated Car Details : \(year) \(color) \(brand)")
}
}
var myCar = Car(brand: "Porsche", year: 2024, color: "Black")
myCar.changeYear(newYear: 2023)
// {Updated Car Details : 2023 Black Porsche} yazdırılır
Ne Nerede Kullanılmalı?
İki veri tipi arasında seçim yapmak aslında tam olarak doğru değil. Çünkü bu durum projenizin bağlamına ve özel gereksinimlerine bağlıdır. Fakat yine basitçe değerlendirmemiz gerekirse;
Struct
- Basit özelliklere sahip basit veri modelleri oluşturulmak istendiğinde
- Oluşturulan nesnelerin birbirinden bağımsız olması istendiğinde
- Güçlü değişmezlik özelliği ile kod güvenliğinin artırılması istendiğinde
- Değer tipi sayesinde maksimum bellek performansına ulaşılmak istendiğinde
Class
- Karmaşık özelliklere ve hiyerarşilere sahip veri modelleri oluşturulmak istendiğinde
- Kalıtım ve çok biçimlilik uygulanmak istendiğinde
- Referans tipi sayesinde paylaşılabilir ve kolay güncellenebilen verilerin oluşturulması istendiğinde
- Referans sayımı ile kaynakların yönetilmesi istendiğinde
Struct ve Class arasındaki farklara baktığınız zaman aslında bu iki veri tipi arasında yapacağınız seçimin keyfi olmayacağını anlayabilirsiniz. Çünkü iki veri tipide kendince güçlü özelliklere sahip durumda. Burada vereceğiniz karar belirttiğimiz gibi tamamen yaptığınız projenin bağlamına, büyüklüğüne, karmaşıklığına ve gereksinimlerine bağlıdır. Bu iki veri tipi arasındaki farkı anlamak, verimli, sürdürülebilir ve ölçeklenebilir Swift projeleri oluşturmanıza olanak tanır. Doğru veri tipi ile oluşturacağınız projeler en iyi performansı gösterecek ve sizi başarıya ulaştıracaktır.