SwiftProgramlamaiOS Geliştirici

Swift derleyicisinin oluşturduğu Codable uyumluluğunun, polymorfik sınıf hiyerarşilerini JSON serileştirmesi üzerinden doğru bir şekilde nasıl turlama yapmasını engellediği nedir?

Hintsage yapay zeka asistanı ile mülakatları geçin

Sorunun cevabı

Oluşturulan Codable uygulaması, yalnızca derleme zamanında mevcut olan statik tür bilgilerine dayanmaktadır. Bir temel sınıf referansı üzerinden heterojen bir sınıf örnekleri koleksiyonunu kodlarken, derleyici yalnızca temel sınıf türüne görünür olan özellikleri serileştiren encode(to:) kodunu oluşturur. Sonuç olarak, alt sınıfa özgü özellikler JSON çıktısından hariç tutulur ve çözümleme sırasında çalışma zamanı, doğru alt sınıfı oluşturmak için gerekli meta veriye sahip olmadığından, temel sınıfa dönüş yapılır ve türle ilgili veriler kaybolur.

Hayattan bir durum

Bir portföy yönetimi için çeşitli işlem türlerini işleyen bir finansal analiz panosu geliştiriyorduk. Alan modeli, Transaction temel sınıfı ve StockTrade, DividendPayment ve FeeCharge gibi alt sınıflar içeren bir sınıf hiyerarşisini kullanıyordu ve bu alt sınıflar tickerSymbol veya dividendRate gibi özel özellikler ekliyordu. Arka uç API'si, her birinin bir transactionType ayırıcı alanı içeren bu işlemlerin karışık bir JSON dizisini döndürüyordu.

Başlangıçta, polymorfik dizi [Transaction] işlemlerini ele alacağını varsayarak Swift’in otomatik Codable sentezine güvendik. Ancak entegrasyon testleri sırasında, [Transaction]'a tür değişimi yapılmış bir [StockTrade] dizisini kodlamanın, sadece id ve amount gibi temel sınıf alanlarını içeren JSON oluşturduğunu keşfettik; tickerSymbol tamamen hariç tutulmuştu. Tersine, bu JSON'u çözümleme, yalnızca temel Transaction örneklerini yeniden oluşturuyor ve alt sınıfa özgü özelliklere erişmeye çalıştığımızda uygulamanın çökmesine neden oluyordu.

Bu sınırı aşmak için üç farklı yaklaşım değerlendirdik. İlk yaklaşım, şifreleme konteynerine açıkça transactionType alanını eklediğimiz ve bu ayırıcı üzerinde geçiş yaparak doğru alt sınıfı başlatacak özel bir init(from:) uyguladığımız manuel Codable uygulamasını içeriyordu. Bu yaklaşım, tam tür güvenliği sağladı ve mevcut nesne grafiğini korudu, ancak her yeni işlem türü için önemli ölçüde tekrar edilebilir kod yazmayı ve saklamayı gerektirdi, bu da yeni özellikler eklenirken geliştirme hatası riskini artırdı.

İkinci çözüm, heterojen türleri bir dizide saklamanın tür mirası olmadan mümkün olduğu AnyCodable sarmalayıcısını veya varlık türleriyle protokol odaklı bir yaklaşımı keşfetmeyi içeriyordu (any TransactionProtocol). Bu, türe dayalı kod güvenliği sağladığı için derleme zamanı tür güvenliğinden ödün verildi ve varlık kutulama ve dinamik iletimden kaynaklanan çalışma zamanı yükü getirildi. Ayrıca, tüketicileri tür silme parçaları ve tür dönüşümleri ile başa çıkmaya zorlayarak kod netliğini azalttı.

Üçüncü seçenek, sınıf hiyerarşisini enum Transaction { case stock(StockData), case dividend(DividendData) } gibi ilişkili değerlerle tek bir enum'ye dönüştürmekti. Enum'ler, oluşturulan Codable ile polymorfik serileştirmeyi doğal olarak destekler, çünkü derleyici otomatik olarak bir ayırıcı alanı oluşturur. Ancak, bu, uygulama genelinde mevcut Core Data modelinin ve iş mantığının kapsamlı bir şekilde yeniden yapılandırılmasını gerektirecekti ve üretim sistemi için kabul edilemez bir regresyon riski taşıyordu.

İlk çözümü - ayırıcı alan ile manuel Codable uygulamasını - seçtik, çünkü bu, mevcut mimariyi veya veritabanı şemasını bozmadan serileştirme katmanındaki değişiklikleri yerelleştirdi. Öncelikle tür tanımlayıcısını çözümleyen bir fabrika yöntemine sahip bir vital sınıf oluşturduk ve ardından dize değeri temelinde uygun alt sınıf başlatıcısına yönlendirdik.

Sonuç, polymorfik API yanıtlarını tam tür sadakatiyle doğru bir şekilde işleyen sağlam bir serileştirme boru hattı oldu. Yaklaşık 200 satırlık manuel ayrıştırma kodu gerektirse de, mevcut özelliklerle geriye dönük uyumluluğu sağladı ve geliştiriciler yeni işlem türleri eklediğinde ancak çözümleme mantığını güncellemeyi unuttuklarında derleme zamanında net hata mesajları verdi, bu da çalışma zamanı hatalarını önledi.

Adayların genellikle kaçırdığı noktalar

Neden bir [Subclass]'ı JSONEncoder ile kodlamadan önce [BaseClass]'a dönüştürmek, alt sınıfa özgü özellikler için veri kaybına neden olur?

Oluşturulan encode(to:) yöntemi, koleksiyondaki değerin derleme zamanı türüne dayalı olarak statik olarak iletilir. [BaseClass]'a dönüştürdüğünüzde, derleyici BaseClass’ın sentezlenen uygulamasını seçer, yalnızca BaseClass'da tanımlanan özellikler üzerinde yineleme yapar. Alt sınıf özellikleri, statik iletim mekanizması oluşturulan yöntemler için dinamik türün meta verilerini dikkate almadığından bu uygulamaya görünmez. Tüm özelliklerin korunması için, ya somut türü kullanarak kodlamalı ya da ayırıcı alan aracılığıyla dinamik tür çözümlemesini manuel olarak uygulamalısınız.

Zorunlu bir başlatıcının gerekliliği, sınıf hiyerarşisindeki Decodable uyumluluğu ile nasıl etkileşir ve bu neden otomatik alt sınıf oluşturmayı engeller?

Decodable, bir init(from: Decoder) başlatıcısını gerektirir. Sınıflar için, bu, alt sınıfların uyumluluğu devralabilmesi için temel sınıfta required olarak işaretlenmelidir. Ancak, temel sınıftaki sentezlenen uygulama, ayırıcı bir alan gibi dış veriye dayalı olarak hangi alt sınıfın başlatılacağını dinamik olarak belirleyemez. Çözücü, bir alt sınıfı temsil eden verilerle karşılaştığında, yalnızca temel sınıf bölümünü başlatmayı bilen temel sınıfın init(from:) yöntemini çağırır. Polymorfik çözümlemeyi desteklemek için geliştiricilerin her alt sınıfta init(from:) yöntemini geçersiz kılması ve yapılandırmadan önce somut türü belirlemek üzere çözücünün konteynerini incelemesi gereken bir fabrika yöntemi uygulaması gerekir.

Swift’in sentezlenen Codable’ı, ilişkili değerleri olan enum'ların nasıl ele aldığı ile sınıf mirası arasındaki temel fark nedir ve bu neden enum'ları polymorfik serileştirme için uygun kılar?

Swift, ilişkili değerleri olan enum'lar için Codable'ı sentezlerken bir ayırıcı anahtar oluşturur. Kodlama, bir dize anahtar olarak durum adını içerir ve çözümleme uygulaması bu anahtar üzerinde geçiş yaparak doğru durumu ve ilişkili yükü yeniden oluşturur. Bu, enum'ların derleme zamanında kapsamlı olarak bilinen kapalı ve mühürlü bir tür hiyerarşisi oluşturmasından dolayı düzgün çalışır ve bu da derleyicinin tamamlayıcı bir geçiş ifadesi oluşturmasına olanak tanır. Aksine, sınıflar açık bir hiyerarşiyi oluşturur; yeni alt sınıflar farklı modüllerde eklenebilir. Derleyici, temel sınıfın Codable uyumluluğunu sentezlerken tüm olası alt sınıflar için kapsamlı bir geçiş oluşturamaz, bu da manuel müdahale olmadan polymorfizmi otomatik olarak ele almayı imkansız kılar.