Geçmiş
Swift'in yansıtma yetenekleri, ABI stabilitesi girişimi sırasında Swift 5.0'da temelde yeniden tasarlandı. Bunun öncesinde, yansıtma her araç zinciri sürümünde değişen kararsız derleyici iç işleyişine dayanıyordu. Mirror API'si, runtime tür denetimi için kararlı, halka açık bir arayüz sağlamak üzere tanıtıldı ve derleme zamanında tür bilgisi olmadan hata ayıklama araçları ve genel günlüğe kaydetme işlemlerini mümkün kıldı. Bu, yapı düzenlerinin sürümler arasında değişebileceği kütüphane evrimine dayanacak bir meta veri biçimi gerektiriyordu.
Problem
Bir yapı dayanıklı olarak işaretlendiğinde (kamu türleri için varsayılan), derleyici saklanan özellikleri için sabit bellek ofsetleri hardcode edemez. Hardcoding, eğer kütüphane yazarı gelecekte alan ekler, kaldırır veya yeniden sıralarsa ikili uyumluluğu bozacaktır. Ek olarak, yansıtma sistemi, türün alan adlarını ve türlerini runtime'da yeniden oluşturacak kadar meta veri açığa çıkarmalıdır ve bu da uygulama detaylarını doğrudan erişimden gizleyen dayanıklı sınırları gözetmelidir.
Çözüm
Swift derleyicisi, ikilinin meta verisinin __swift5_fieldmd bölümüne alan tanımlayıcıları yayar. Bu tanımlayıcılar sabit ofsetler içermez; bunun yerine nispi ofset erişimcileri veya başlatma zamanı düzen hesaplamaları depolarak gerçek bellek konumunu runtime'da çözer. Dayanıklı türler için, meta veri, tür mevcut süreçte başlatıldığında doldurulan bir alan ofset vektörü içerir. Bu dolaylılık, Mirror API'sinin, runtime'da yüklenen kütüphanenin belirli sürümüne uyacak şekilde hesaplanan ofsetleri kullanarak özellikleri geçmesini sağlar ve böylece hem ABI stabilitesini hem de yansıtma yeteneklerini korur.
import Foundation struct ResilientConfig { let timeout: Double private let apiKey: String // 'private' olmasına rağmen Mirror'a erişilebilir } let config = ResilientConfig(timeout: 30.0, apiKey: "secret") let mirror = Mirror(reflecting: config) for child in mirror.children { print("Property: \(child.label ?? "unnamed"), Value: \(child.value)") }
Modüler bir iOS uygulama mimarisi, Ağ modülünü (kapalı kaynak SDK) Analitik modülünden (iç kaynak) ayırır. Ağ modülü, kamu erişiminden geçirilemeyecek özel kimlik doğrulama belirteçlerini içeren karmaşık yapılandırma yapılarını döndürür; ancak Analitik ekibi, ara sıra zaman aşımını hata ayıklamak için tüm yapılandırma parametrelerini günlüğe kaydetmeye ihtiyaç duyar.
Çözüm 1: Kamu Sözlüğü Dönüşümü
Ağ ekibi, alanları stringlere manuel olarak eşleştiren bir toDictionary() metodunu açığa çıkarabilir.
Artılar: Derleme zamanı tür güvenliği, açığa çıkarılan veriler üzerinde açık kontrol, hızlı performans.
Eksiler: Yapı her değiştiğinde bakım gerektirir; istemcinin yeniden derlenmesi olmadan SDK güncellemelerinde eklenen yeni alanları yansıtamaz; geliştirici, filtrelemeyi unutursa hassas alanları açığa çıkarır.
Çözüm 2: Objective-C Runtime İç Gözlem
NSObject köprüleme aracılığıyla valueForKey: kullanmak.
Artılar: Objective-C geçmişine sahip geliştiricilere aşina.
Eksiler: Swift yapıları NSObject alt sınıfları değildir; @objc uyumluluğunun zorlanması, değer anlamını referans anlamına dönüştürür ve ikili boyutu önemli ölçüde artırır; yerel Swift türleri ile çalışmaz.
**Çözüm 3: Swift Yansıtması aracılığıyla Mirror
Erişim kontrolünden bağımsız olarak tüm saklanan özellikleri döngüyle geçmek için Mirror(reflecting:) kullanarak genel bir günlüğe kaydedici uygulamak.
Artılar: SDK güncellemelerinde yenilenen özelliklere, yeniden derleme olmadan otomatik olarak adapte olur; dayanıklı sınırları gözetir; değer türleri ve genel kod ile çalışır.
Eksiler: Mirror, iç depolaması için bellek ayırır, bu da yüksek frekanslı günlüğe kaydetme için uygun değildir; erişim kontrolünü aşar, bu da filtrelenmediği takdirde özel sırların açığa çıkmasına neden olabilir; C bit alanlarını veya hesaplanan özellikleri yansıtamaz.
Seçilen Çözüm
Ekip, CustomReflectable uyumluluğunu kontrol eden bir sarmalayıcı kullanarak Çözüm 3'ü benimsedi; böylece Ağ SDK'sının sterilize edilmiş bir görünüm sağlamasına izin verilmiş oldu. Ağ modülü, apiKey'i hariç tutan customMirror'ı uygulayarak timeout ve diğer güvenli alanları açığa çıkardı.
Sonuç
Analitik modülü, üç büyük SDK güncellemesinde yapılandırma durumlarını başarıyla günlüğe kaydetti; ancak Ağ ekibi, bit alanları içeren düşük seviyeli soket seçenekleri için bir C yapı sarmalayıcısı eklediğinde, bu özel alanlar günlüğe boş olarak göründü. Bu, Mirror kısıtlamasını açıklamak için dokümantasyon gerektirdi; diğer tüm yapılandırmalar ise otomatik olarak yansımaya devam etti.
Mirror, kendine atıfta bulunan veri yapılarında sonsuz döngüyü nasıl önler, ve CustomReflectable uygularken geliştiriciye ne sorumluluk düşer?**
Mirror, yansıtma yürüyüşü esnasında sınıf örneklerinin kimliğini izleyerek referans döngülerini tespit eder. Bir sınıf örneği ile karşılaştığında, o nesnenin mevcut döngü yığasında zaten bulunup bulunmadığını kontrol eder; eğer öyleyse, yığın taşmasını önlemek için dolaşımı durdurur. Değer türleri için, yalnızca döngüleri oluşturacak referanslar içeriyorlarsa döngü oluşur. Ancak, bir geliştirici CustomReflectable'ı uygulayıp children ile özel bir Mirror oluşturarak döngü oluşturursa, runtime bu özel yapıda döngüleri tespit edemez. Geliştirici, children dizisinin sonsuz döngüler yaratmadığından emin olmalıdır; örneğin, bir döngü derinliği sınırını kontrol ederek veya özel yansıtma oluştururken kendi ziyaret edilen kümesini tutarak.
Mirror ile bir struct üzerinde yansıma yaptığında, özellikle C bit alanları veya birleşimler içeren struct'lar üzerinde neden farklı bellek düzenleri bildirdiğini açıklayın. Swift'in yansıtma meta verisi, Swift türleri için tasarlanmıştır ve C ile etkileşim için Clang içe aktarıcı meta verisini kullanır. C bit alanları ve birleşimler, kararlı adreslere sahip ayrı Swift saklanan özellikleri şeklinde temsil edilmez; opak depolama veya Clang içe aktarıcısının tür çevirisi sırasında satır içi dolgu olarak temsil edilir. Mirror API'si children koleksiyonunu oluşturmak için adreslenebilir alanlar gerektirir. Sonuç olarak, bit alanları, __swift5_fieldmd bölümünde alan tanımlayıcıları olmadığından yansıma için görünmez; birleşim üyeleri ise ilgili durumları tanımlayan meta verinin birleşim konteynerini tanımladığı için üst üste gelebilir veya yanlış türde görünebilir. Bu, temel bir kısıtlamadır: Mirror, türün Swift görünümünü yansıtır, temel C düzenini değil.
Mirror üzerinden özellik erişiminin doğrudan erişime kıyasla performans maliyeti nedir ve neden özellik sayısının okunması ile özellik değerlerinin okunması arasında maliyet asimetrik?**
Özelliklere Mirror aracılığıyla erişmek, doğrudan erişime göre kat kat daha yavaştır çünkü bu, runtime meta veri aramaları, Mirror örneği için yığın tahsisi ve tür meta verisinde depolanan alan erişim işlevleri aracılığıyla dolaylı aramalar gerektirir. children sayısını okuma, saklanan özelliklerin sayısını belirlemek için alan tanımlayıcı meta verisini ayrıştırmayı gerektirir ve bu, __swift5_fieldmd bölümünün hızlı bir taraması olarak kabul edilir. Ancak, gerçek değerlere erişmek, her alan için değer tanıkları veya özel erişim işlevleri çağırmayı içerir; bu, veri kopyalamayı, ARC türleri için referans sayımlarını yönetmeyi ve dayanıklı sınırları aşmayı gerektirebilir. Sınıflar için bu maliyet, Objective-C runtime kontrollerini de içerir. Bu nedenle, değerleri çıkarmak için mirror.children üzerinden döngüye girmek, mirror.children.count'ı kontrol etmekten daha yüksek bir maliyet doğurur; bu da Mirror'ı yüksek performans gereksinimleri olan yollar için uygun hale getirmez, ancak hata ayıklama için yararlı olmasını sağlar.