Swift dilinde her veri tipi bu iki kategoriden birine girer.
Bu ayrım, verinin bellekte (RAM) nerede saklandığını ve kopyalanma davranışını belirler.
- Temel Ayrım ve Bellek Alanları
- Reference Types (Class) Analizi: "Shared State"
- Class vs Struct Farkı
- 📊 Class vs Struct – Detaylı Karşılaştırma Tablosu
- 📌 Mimari Özet (Altın Kural)
- 🎯 Mülakat Tek Cümlelik Özet
- 🎯 iOS Mülakat Soruları – Class vs Struct
- 📌 Kısa Özet
Hafızayı iki ana bölge olarak yönetiriz: Stack ve Heap.
Verinin kendisinin doğrudan saklandığı yapılardır.
StructEnumInt,Double,BoolTupleStringArrayDictionary
- Stack bölgesinde tutulur.
- Stack çok hızlıdır (LIFO – Last In First Out).
- İşletim sistemi tarafından otomatik yönetilir.
- Değişken scope dışına çıktığı anda bellekten anında silinir.
- ARC maliyeti yoktur.
- Kopyalama (Copy) mantığı ile çalışır.
Verinin kendisi değil, verinin adresi (pointer) saklanır.
ClassClosureActor
- Heap bölgesinde tutulur.
- Heap büyük ve karmaşık bir bellek havuzudur.
- Veriye erişim, Stack’e göre daha maliyetlidir.
- ARC (Automatic Reference Counting) tarafından yönetilir.
- Paylaşım (Shared Instance) mantığı ile çalışır.
Bir Class örneğini başka bir değişkene atadığınızda, Swift yeni bir nesne yaratmaz.
Sadece o nesnenin hafıza adresini (reference) kopyalar.
Bu nedenle iki değişken de aynı nesneyi işaret eder.
Teknik Terim: Pointer Copy (İşaretçi Kopyalama)
class Bilgisayar {
var model: String
var ram: Int
init(model: String, ram: Int) {
self.model = model
self.ram = ram
}
}
// 1. Heap'te bir nesne oluştu, 'mac1' buna referans veriyor.
var mac1 = Bilgisayar(model: "MacBook Pro", ram: 16)
// 2. REFERANS kopyalandı. 'mac2' de AYNI nesneye bakıyor.
var mac2 = mac1
// 3. mac2 üzerinden RAM'i artırdık.
mac2.ram = 32
// SONUÇ: mac1 etkilendi mi?
print("Mac1 Ram: \(mac1.ram)")
// ÇIKTI: 32
// (Çünkü onlar aslında aynı bilgisayar.)Çoklu thread (Multi-threading) ortamlarında, Reference Type’lar Race Condition hatalarına açıktır. Bunun nedeni:
- Aynı nesnenin birden fazla yerden, aynı anda, kontrolsüz şekilde değiştirilebilmesidir. Bu yüzden paylaşılan mutable state içeren class’lar, concurrency açısından dikkatli tasarlanmalıdır.
Reference Type’ların en önemli özelliği identity (kimlik) kavramıdır.
İki değişkenin aynı veriye mi, yoksa sadece eşit değerlere mi sahip olduğunu ayırt edebiliriz.
Swift’te:
==→ Değer eşitliği===→ Aynı nesne mi? (Aynı hafıza adresi)
class User {
let id: Int
init(id: Int) { self.id = id }
}
let user1 = User(id: 1)
let user2 = user1
let user3 = User(id: 1)
print(user1 === user2) // true → aynı nesne
print(user1 === user3) // false → farklı nesneler📌 Önemli:
===operatörü sadece Class (Reference Type)’lar için vardır.- Value Type’larda identity kavramı yoktur.
Reference Type’lar paylaşılan mutable state oluşturur. Bu şu anlama gelir:
- Bir yerden değiştirilen veri
- Başka bir yeri yan etkili (side-effect) şekilde etkiler
func upgradeRAM(_ bilgisayar: Bilgisayar) {
bilgisayar.ram += 16
}
upgradeRAM(mac1)
print(mac2.ram) // 48- Fonksiyon sadece
mac1’i değiştiriyor gibi görünür ama aslında tüm referans sahiplerini etkiler. - Bu durum: Debug sürecinin zorlaşmasına, beklenmeyen UI bug’larına, concurrency (eşzamanlılık) problemlerine yol açar.
Reference Type’lar bilinçli kullanıldığında son derece güçlüdür.
Ancak yanlış yerde kullanıldıklarında yan etki (side-effect), memory leak ve
concurrency problemlerine yol açabilirler.
Bu yüzden class tercih etmeden önce kendine şu soruları sormalısın:
Aşağıdaki durumlardan en az biri geçerliyse class doğru tercihtir:
-
Nesnenin bir kimliği (identity) varsa
→ Aynı nesnenin uygulamanın farklı yerlerinden tek bir varlık olarak görülmesi gerekiyorsa -
Nesne paylaşılıyorsa → Birden fazla bileşen aynı instance üzerinde çalışıyorsa
-
Nesnenin yaşam döngüsü önemliyse
→ Oluşturulması, tutulması ve yok edilmesi kontrol edilmeliyse -
ARC ilişkileri gerekiyorsa
→weak,unowned,delegategibi referans yönetimleri söz konusuysa
📌 Tipik class örnekleri:
UIViewControllerCoordinatorManagerServiceNetwork / Cache / DataSourcekatmanları
Aşağıdaki durumlarda class kullanmak kötü bir tercihtir:
-
Nesne sadece veri taşıyorsa → Davranışı yoksa veya çok sınırlıysa
-
Nesnenin immutable (değişmez) olması gerekiyorsa
-
Thread-safe olması kritikse
→ Çoklu thread ortamında güvenli çalışması gerekiyorsa -
Yan etkisiz (side-effect free) davranış isteniyorsa
📌 Bu durumlarda struct tercih edilmelidir.
Struct’lar değer tipleri (Value Types) oldukları için, çoğu durumda class yerine daha güvenli ve öngörülebilir bir kullanım sunarlar.
Aşağıdaki durumlarda struct tercih edilmelidir:
-
Nesne sadece veri taşıyorsa → Davranışı yok veya çok sınırlıysa
-
Nesnenin immutable (değişmez) olması gerekiyorsa
-
Thread-safe olması kritikse
→ Çoklu thread ortamında güvenli çalışması gerekiyorsa -
Yan etkisiz (side-effect free) davranış isteniyorsa
📌 Tipik struct örnekleri:
- Model katmanındaki veri objeleri
- Data Transfer Object (DTO)
- SwiftUI State veya ViewModel’lerde küçük immutable yapı taşları
- Immutable koleksiyonlar (
Array,Dictionarygibi kopyalanabilir yapılar)
Swift’te mimari olarak yaygın ve sağlıklı yaklaşım şudur:
- Model katmanı →
struct - Controller / Coordinator / Manager →
class
Bu ayrım sayesinde:
- Veriler güvenli ve öngörülebilir olur
- Yan etkiler minimize edilir
- Debug ve test süreçleri kolaylaşır
- Concurrency problemleri büyük ölçüde azalır
Davranışı olan, kimliği olan ve yönetilen nesneler →
class> Sadece veri olan, kopyalanabilir ve güvenli nesneler →struct
Swift’te class ve struct arasındaki fark yalnızca sözdizimsel değildir.
Bu iki yapı, bellek yönetimi, veri güvenliği, mimari kararlar ve eşzamanlılık (concurrency) açısından kökten farklı davranır.
Bu farkı doğru anlamak, iOS mimarisinde doğru veri yapısını seçmenin temelidir.
- Struct → Değerin kendisini temsil eder - Class → Bir nesnenin kimliğini (identity) temsil eder
Struct’ta önemli olan ne olduğu,
Class’ta önemli olan hangi nesne olduğudur.
Struct (Value Type) - Değer bazlıdır
- Sahibi yoktur
- Kapsam (scope) bittiğinde otomatik olarak yok olur
- ARC ile izlenmez
- Bellek yaşam döngüsü basit ve deterministiktir
Class (Reference Type) - Kimlik sahibidir (identity)
- Birden fazla yerden paylaşılabilir
- Bellekten ne zaman silineceği referans sayısına bağlıdır
- ARC tarafından yönetilir
- Yanlış kullanıldığında memory leak riski taşır
Struct
- Her kullanım kendi kopyasıyla çalışır
- Yan etki üretmez
- Bir yerdeki değişiklik başka bir yeri etkilemez
- Predictable (öngörülebilir) davranır
Class
- Paylaşılan mutable state oluşturabilir
- Bir yerdeki değişiklik başka yerleri etkileyebilir
- Yan etki üretme riski yüksektir
- Debug ve test süreçleri daha zordur
Struct
- Varsayılan olarak thread-safe davranışa daha yakındır
- Aynı veriye eşzamanlı erişim riski düşüktür
- Swift Concurrency (Sendable, Actor) ekosistemiyle doğal uyumludur
Class
- Race condition riskine açıktır
- Eşzamanlı erişim mutlaka kontrol edilmelidir
- Genellikle
actor,lockveya serial queue gerektirir
Struct tercih edilir:
- Model katmanında
- Data transfer objelerinde (DTO)
- Immutable veri yapılarında
- State yönetiminde (SwiftUI, Redux benzeri mimariler)
Class tercih edilir:
- ViewController
- Coordinator
- Manager / Service
- Network, Cache, Session gibi yaşam döngüsü önemli nesnelerde
📌 Swift ekosisteminde bu yüzden yaygın ayrım şudur:
- Model → Struct - Controller / Manager → Class
Aşağıdaki tablo, Class ve Struct arasındaki farkları yalnızca yüzeysel değil;
bellek, mimari, concurrency ve mülakat perspektifiyle net şekilde karşılaştırır.
| Karşılaştırma Kriteri | struct (Value Type) |
class (Reference Type) |
|---|---|---|
| Temel Anlam | Değerin kendisini temsil eder | Nesnenin kimliğini (identity) temsil eder |
| Bellekte Tutulma | Stack (çoğunlukla) | Heap |
| Sahiplik (Ownership) | Yok | Var |
| Paylaşım | Paylaşılmaz (kopyalanır) | Paylaşılır (aynı instance) |
| Kopyalama Davranışı | Copy Semantics | Reference (Pointer) Semantics |
| Yan Etki (Side Effect) | ❌ Üretmez | |
| Debug Kolaylığı | ✅ Çok kolay | |
| Thread Safety | ✅ Doğal olarak daha güvenli | ❌ Race condition riski |
| Concurrency Uyumu | ✅ Swift Concurrency ile doğal | |
| ARC ile Yönetim | ❌ Hayır | ✅ Evet |
weak / unowned Kullanımı |
❌ Yok | ✅ Var |
| Memory Leak Riski | ❌ Yok | |
| Performans | ✅ Hızlı ve deterministik | |
| Immutable Kullanım | ✅ Çok uygun | |
| Kimlik (Identity) | ❌ Yok | ✅ Var |
| Yaşam Döngüsü Kontrolü | ❌ Yok | ✅ Var |
| Kalıtım (Inheritance) | ❌ Yok | ✅ Var |
| Protokol Uyumu | ✅ Var | ✅ Var |
| SwiftUI Uygunluğu | ⭐⭐⭐⭐⭐ | ⭐⭐ |
| UIKit Uygunluğu | ⭐⭐ | ⭐⭐⭐⭐⭐ |
-
Struct kullan: - Veri taşıyorsa
- Immutable olması gerekiyorsa
- Yan etki istenmiyorsa
- Concurrency önemliyse
-
Class kullan: - Nesnenin kimliği varsa
- Paylaşılan state gerekiyorsa
- Yaşam döngüsü kontrol edilecekse
- Delegate, weak, unowned ilişkileri gerekiyorsa
“Struct veri içindir, Class davranış ve kimlik içindir.”
Bu tabloyu zihninde netleştiren bir iOS Developer:
- Memory management’ı
- Concurrency risklerini
- Mimari kararları
çok daha bilinçli verir.
Aşağıdaki sorular bu konudan doğrudan gelir ve mülakatlarda sıkça sorulur.
Cevap: Struct’lar değer bazlıdır, yan etki üretmez ve thread-safe davranışa daha yakındır.
Bu sayede debug, test ve concurrency yönetimi daha güvenlidir.
Cevap: Hayır. Kimliği olan, paylaşılan ve yaşam döngüsü önemli nesneler için Class gerekir.
Örneğin ViewController, Service veya Manager gibi yapılar Struct ile temsil edilemez.
Cevap: Paylaşılan mutable state nedeniyle side-effect üretmesi ve yanlış referans ilişkileri sonucu memory leak oluşmasıdır.
Cevap: Çünkü SwiftUI state bazlı çalışır.
Struct’lar copy semantics sunduğu için UI yeniden çizimleri deterministic ve güvenlidir.
Cevap: Şu soruları sorarım:
- Kimliği var mı?
- Paylaşılıyor mu?
- Yaşam döngüsü önemli mi?
- Yan etki üretmesi kabul edilebilir mi?
Eğer cevaplar hayır ise → Struct
evet ise → Class
- Struct → Güvenli, basit, yan etkisiz
- Class → Güçlü ama dikkatli kullanılmalı
- Yanlış seçim → Debug kabusu
- Doğru seçim → Temiz mimari