Derleme zamanı veri yarışı denetimi: Sendable vs runtime disiplin
Swift 6'nın en radikal kararı, veri yarışını runtime hatası olmaktan çıkarıp derleme hatasına dönüştürmesi. SE-0302 önerisi Sendable'ı "concurrency domain'leri arası kopyalanarak güvenle taşınmasına izin verilen tipleri modelleyen" bir protokol olarak tanımlıyor. Derleyici, Sendable'a uymayan bir tipi actor sınırları arasında geçirmeyi reddediyor; @Sendable closure'lar da yalnız by-value capture kullanabiliyor. Resmi vaat: Swift 6 modunda derleyici "concurrent programs are free of data races" garantisi verebiliyor. Bedeli var: yıllardır fark edilmeyen paylaşılan mutable state, göç sırasında derleme hatası olarak yüzeye çıkıyor.
Kotlin coroutines bu garantiyi hiç vermiyor — bilinçli bir tasarım tercihi. kotlinlang.org resmi rehberi "Shared mutable state and concurrency"yi ayrı başlık olarak ele alıyor: paylaşılan state'in yönetimi derleyicinin değil geliştiricinin sorumluluğunda — Mutex.withLock, immutable veri yapıları veya tek-thread dispatcher gibi disiplinlerle çözülüyor.
Sonuç: Swift "önce acı çek, sonra güvende ol"; Kotlin "esnek kal, disiplini sen kur" diyor. Doğru seçim ekip büyüklüğüne bağlı.
Yapılandırılmış eşzamanlılık ve iptal modeli
İki modelin en yakın örtüştüğü kriter. SE-0304 kuralı net: bir child task oluşturan fonksiyon, dönmeden önce onun bitmesini beklemek zorunda ve bir task tek seferde tek fonksiyon çalıştırır — "a single task has no concurrency". Parent hata fırlatırsa awaitlenmemiş child task'lar otomatik iptal edilir.
Kotlin'de aynı fikir coroutineScope() ve Job hiyerarşisiyle uygulanır (resmi "Coroutine scope and structured concurrency" bölümü). İptal, resmi dokümana göre coroutine'in kendi kontrol noktalarında fark ettiği kooperatif bir mekanizmadır: askıya alma noktası olmayan CPU-yoğun bir döngü, iptal edilse bile isActive ya da ensureActive() ile kontrol etmedikçe çalışmaya devam eder.
Yaygın bir yanılgıyı düzeltelim: Swift'te de iptal kooperatiftir. SE-0304'ün ifadesiyle "cancellation is cooperative: the task will note that it has been cancelled and can choose to return earlier". Task.isCancelled ve try Task.checkCancellation(), isActive/ensureActive() ikilisinin birebir karşılığıdır. Asıl fark fark edişte değil yayılmada: Swift'te yapısal iptal yayılımı dil-seviyesinde zorunlu, Kotlin'de doğru scope seçimine bağlı. İptal kontrolünü unutma hatasını iki tarafta da yapabilirsin.
Aktör vs dispatcher/scope zihinsel modeli
Swift'in actor'ı SE-0306 ile gelen dil-seviyesi birinci sınıf bir yapı: "her actor kendi verisini izole eder, aynı anda yalnız tek thread erişir" ve "mailbox'ındaki mesajları sırayla işler — iki actor-isolated görev asla eşzamanlı çalışmaz". Bu, klasik lock/mutex disiplinini dil seviyesinde otomatikleştiren bir soyutlama. Actor'lar arası geçen tüm referansların Sendable olması da şart — actor modeli Sendable disipliniyle iç içe.
Kotlin'de bunun dil-seviyesinde birebir karşılığı yok. En yakın model dispatcher (hangi thread havuzu) + CoroutineScope (yaşam döngüsü) ikilisi — resmi "Coroutine dispatchers" ve "Comparing coroutines and JVM threads" başlıkları bunu anlatıyor. Paylaşılan state'i izole etmek isteyen bir Kotlin geliştiricisi ya tek-thread'li dispatcher'a confine eder ya Mutex kullanır ya da actor-benzeri bir desen kurar — ama hiçbiri dilin kendisinde gömülü değil.
Bu fark öğrenme eğrisini de şekillendiriyor: Swift'te "actor" öğrenince izolasyon otomatik gelir; Kotlin'de aynı garantiyi elde etmek için birkaç aracı doğru kombinasyonda kullanmayı öğrenmek gerekir. Coroutine context'in "parental responsibilities" kavramı bu ikili modelin bir parçası.
Akış soyutlaması: AsyncSequence vs Flow
Apple'ın resmi dokümantasyonu AsyncSequence'ı net tanımlıyor: "Sequence'e benzer ve asenkronluk ekler... değerleri await ile alırsınız". AsyncIterator'ın next() metodu async — çağıran taraf bir sonraki değeri await ile bekliyor. Bu, mevcut Sequence/Iterator protokolüne minimal bir ek.
Kotlin'in Flow'u daha zengin: resmi dokümana göre bir Flow "asenkron üretilebilen ardışık bir değer akışı". Asıl fark cold/hot ayrımında: dokümanlar açıkça "Cold flows" ve "Hot flows" başlıklarını ayırıyor — StateFlow/SharedFlow gibi hot flow'lar birden fazla collector arasında paylaşılan, sürekli var olan bir durumu temsil ediyor. Cold flow'lar her yeni collector için sıfırdan çalışıyor.
AsyncSequence'ın resmi API'sinde bu hot/cold ayrımı dil-düzeyinde adlandırılmış değil — benzer işlevi AsyncStream veya swift-async-algorithms sağlıyor, ama StateFlow'un "her zaman güncel değer tutan, paylaşılan" semantiğinin birebir karşılığı yok. Bu, KMP köprülemede sürtünme kaynaklarından biri: StateFlow'u AsyncSequence'a köprülerken hot-flow davranışını kaybetme riski var, köprü katmanında elle test gerekir.
Mevcut kod tabanının göç maliyeti
Swift 6'nın göç stratejisi kademeli tasarlanmış. Migration guide üç garanti veriyor: dil modu "entirely under your control on a per-target basis", derleyici dört dil modunu aynı anda destekliyor ("6", "5", "4.2", "4") ve farklı modlarla derlenen hedefler birbiriyle interop edebiliyor. Pratikte en izole modülden başlayıp kademeli yayılabilir — "big bang" geçiş zorunlu değil. Dil modu tamamen opt-in: mevcut projeler yapılandırma değiştirmeden strict moda geçmiyor.
Kotlin'de "dil modu" kavramı yok çünkü coroutines kütüphane seviyesinde. Suspend fonksiyonlar mevcut callback/Future koduna noktasal eklenebiliyor — resmi rehber bunu "futures/promise'lardan daha güvenli ve az hataya açık" bir soyutlama olarak tanımlıyor, ama benimseme "hepsi ya da hiçbiri" değil. Göç maliyeti daha çok organizasyonel: yapılandırılmış eşzamanlılık disiplinini (GlobalScope.launch'tan kaçınma, doğru scope seçimi) ekip genelinde dayatmak, derleyicinin değil code review'ın işi.
Kısacası: Swift'in maliyeti derleyici hatalarıyla ölçülebilir ve somut; Kotlin'inki daha soyut ve disiplin bağımlı — kaçırılan bir GlobalScope.launch production'a kadar fark edilmeyebilir.
Hata ayıklama ve yığın izi kalitesi
Kotlin bu kriterde IDE entegrasyonu üzerinden avantajlı: kotlinlang.org, IntelliJ IDEA için "Debug coroutines using IntelliJ IDEA" tutorial'ı yayınlıyor ve coroutine'lere özgü "Optimized-out variables" sorununu ele alıyor. CoroutineName ile coroutine'leri isimlendirip log çıktısında ayırt etmek de resmi öneri.
Swift tarafında resmi rehber yok değil; iş ikiye bölünüyor. Birincisi Xcode'un sanitizer dokümanı: "Thread Sanitizer—The TSan tool detects race conditions between threads" ve aynı sayfaya göre bu araçlar "also support the Swift language". Sınırı da orada yazıyor: TSan'i cihazda çalıştıramazsın, yalnız 64-bit macOS uygulamanda ya da Simulator'da koşan iOS/iPadOS/tvOS/visionOS/watchOS uygulamanda kullanabilirsin. İkincisi Swift 6 göç rehberinin "Common Compiler Errors" bölümü; concurrency hatalarını kök nedenlerine göre gruplayıp "Most of these can be tracked down to a much smaller set of root causes" diyor.
Doğru okuma: Kotlin'in üstünlüğü kaynak varlığı değil, debugger'ın doğrudan IDE'ye gömülü olması. Swift'te yük derleme zamanına kaymış; kalan runtime yarışlarını TSan ile avlıyorsun — cihazda koşturamadığın için yarış senaryolarını Simulator'da tekrarlanabilir kıl.
KMP'de iki modelin köprülenmesi
Kotlin/Native, Swift/Objective-C dünyasıyla doğrudan etkileşiyor ve bu resmi belgelenmiş: native-arc-integration.html "Threads", "Deinitializers", "Completion handlers" ve "Retain cycles" başlıklarıyla Kotlin GC'si ile Swift/Obj-C'nin ARC modeli arasındaki entegrasyonu anlatıyor — iki bellek yönetimi felsefesinin aynı runtime'da bir arada çalışması gerekiyor, retain-cycle riski özellikle dikkat istiyor.
Swift export tarafında somut ilerleme var: Kotlin 2.4.0 sürüm notlarının "Swift export goes Alpha with improved concurrency support" başlığı, suspend fonksiyonların Swift'in idiomatik async karşılıklarına ve kotlinx.coroutines flow'larının AsyncSequence'a çevrildiğini duyuruyor — coroutine'lerin Swift tarafına yansıması JetBrains'in öncelik listesinde, ama köprü hâlâ Alpha.
Pratik kararlar: (1) Kotlin suspend fonksiyonları Swift export yolunda async fonksiyon, klasik Objective-C interop yolunda completion-handler olarak görünür — hangi yolda olduğunu baştan kontrol et. (2) StateFlow'u AsyncSequence'a köprülerken hot-flow semantiğini korumak için ek bir adaptör katmanı gerekir. (3) Retain-cycle riskini azaltmak için resmi ARC entegrasyon rehberini baştan oku.