SwiftProgramlamaSwift Geliştirici

Swift'in dolar sembolü ön eki sözdizimini sağlayan ve projectedValue'nin modül sınırları boyunca referans anlamını açığa çıkarması durumunda tip güvenliğini nasıl sağladığını belirten sentezlenmiş depolama ve erişim deseni nedir?

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

Soruya Cevap

Swift 5.1, tekrarı azaltmak amacıyla erişimcileri ele almak için SE-0258 aracılığıyla özellik sarmalayıcılarını tanıttı. projectedValue gerekliliği, sarmalanmış değerinin ötesinde SwiftUI'nin Binding'i veya doğrulama durumları gibi ikincil API yüzeylerini açığa çıkarmak için tasarlanmıştır. Bu özellik, geliştiricilerin $ ön ek sözdizimini kullanarak meta veriye veya projeksiyonlara erişmesini sağlar.

Sorun, Swift'in deklare edilmiş sözdizimini geçerli SIL (Swift Ara Dil) hâline dönüştürmesi gerektiğidir; bu, isim çakışmalarını veya erişim denetimini ihlal etmemelidir. Derleyici, sarmalanmış özellik için değer anlamsallığını korurken projeksiyon aracılığıyla referans anlamını potansiyel olarak açığa çıkaran depolama sentezlemelidir; tüm bunları gerçekleştirirken $ ön ekli belirleyicinin kullanıcı tanımlı üyelerle çakışmamasını sağlamalıdır.

Çözüm, kaynak-dan-kaynağa sözdiziminden arındırmadır. @Wrapper var property: T olarak tanımlanan bir özellik için derleyici üç ayrı üye oluşturur. İlk olarak, Wrapper<T> türünde özel bir depolama değişkeni _property oluşturulur. İkinci olarak, _property.wrappedValue'ya get/set işlemlerini yönlendiren bir hesaplanmış özellik property oluşturulur. Üçüncü olarak, _property.projectedValue'yi döndüren hesaplanmış bir özellik $property oluşturulur. $ ön ekli özellik, orijinal deklare etme erişim kontrolünü miras alır ve derleyici, $ söz dizimi kullanıldığında projectedValue'nin mevcut olmasını zorunlu kılar.

@propertyWrapper struct Validating<T> { var wrappedValue: T var projectedValue: ValidationState<T> init(wrappedValue: T) { self.wrappedValue = wrappedValue self.projectedValue = ValidationState(value: wrappedValue) } } // Söz diziminden arındırılmış hâli: struct Form { private var _username: Validating<String> var username: String { get { _username.wrappedValue } set { _username.wrappedValue = newValue } } var $username: ValidationState<String> { get { _username.projectedValue } } }

Hayattan Bir Durum

Tıbbi veri girişi uygulaması tasarlıyorduk; burada her alan, mevcut değerinin yanı sıra önceki hataları ve düzeltme zaman damgalarını içeren karmaşık bir doğrulama geçmişini takip etmeliydi. Bu zorluk, tek bir özellik soyutumundan iki ayrı veri yolunun açığa çıkarılmasını gerektiriyordu: UI metin alanı için ham dize ve analizler ile hata görüntüleme için doğrulama geçmişi.

İlk düşünülen yaklaşım, özellik adlarını ValidationHistory nesnelerine eşleyen paralel bir sözlük tutmaktı. Bu, depolama esnekliği sağlasa da, refaktörleştirme sırasında bozulan ve sözlük ile gerçek özellik değerleri arasında manuel senkronizasyon gerektiren dize türünden API'ler getiriyordu. Senkronizasyon hatalarının, tıbbi veriler için kabul edilemeyecek derecede yüksek bir risk oluşturduğu görüldü.

İkinci yaklaşım, hem değeri hem de geçmişi içeren bir sarmalayıcı yapı oluşturmaktı; ardından o bileşik türü özellik türü olarak kullanmaktı. Tür güvenli olsa da, bu, alan modelini doğrulama endişeleri ile kirletti ve her erişim noktasının açığa çıkarılmasını sağlamayı zorunlu kıldı; bu, UI ve iş mantığı arasında temiz mimari ayrımı sağlama amacını bozan bir durumdu.

Üçüncü yaklaşım, projectedValue'nin bir ValidationHistory referans türü döndürdüğü özel bir @Validated özellik sarmalayıcısı kullanmaktı. Bu içeride senkronizasyonu kapsülleyerek $fieldName'in tarih erişimi için kullanılmasını sağladı. Bunu seçmemizin sebebi, sarmalanmış dize değerinin CoW (Copy-on-Write) anlamsallığını koruması ve doğrulama geçmişi için kararlı bir referans kimliği sağlamasıydı, bu sayede UI bileşenleri değişiklikleri takibe alabiliyor ve kopyalama yükü olmuyordu.

Sonuç, senkronizasyon hatalarının tümünü ortadan kaldırdı ve doğrulama ile ilgili kod tabanını %35 oranında azalttı. $ sözdizimi, genç geliştiriciler için sezgisel bir keşif sağladı ve derleme zamanı uygulaması, modül sınırları boyunca uygulama detaylarının kazara açığa çıkmasını önledi.

Adayların Sıklıkla Gözden Kaçırdığı Noktalar

Dolar sembolü ön eki aracılığıyla erişildiğinde, değer türü projectedValue'ye yapılan mutasyonlar neden kalıcı değil?

Eğer özellik sarmalayıcı bir yapı ise, projectedValue alıcı bir kopyasını döndürür. Eğer projectedValue bir yapı (örneğin, bir Int veya özel bir doğrulama durumu yapısı) döndürüyorsa, $property.errorCount += 1 gibi ifadeler, hemen imha edilen geçici bir kopyayı değiştirir. Kalıcı mutasyonları sağlamak için, projectedValue bir referans türü döndürmeli veya sarmalayıcı bir sınıf tabanlı depolama ile CoW'yi uygulamalıdır. Alternatif olarak, dolaylılık sağlayan bir Binding veya değişken işaretçi döndürmelidir. Başlangıç seviyesindeki geliştiriciler sıklıkla $property'nin sarmalayıcının iç durumda değişken erişim sağladığını varsayıyor, ancak Swift'in değer anlamsallığını dikkate almıyorlar.

Sentezlenen dolar sembolü özelliğinin erişim kontrolü, orijinal özelliğin erişim seviyesi ile nasıl etkileşiyor?

Derleyici, $ ön ekli özelliği, orijinal özelliği ile aynı erişim kontrolü ile sentezler. Eğer public @Wrapper var name: String olarak tanımlarsanız, hem name hem de $name public olur. Aksine, private özellikler private projekte değerler üretir. Adaylar sıklıkla sarmalanmış değeri public yapmak istemekte, projekte değeri ise içsel veya özel tutmaya çalışmaktadır; bu, mevcut Swift versiyonlarında imkansızdır. Çözüm, özelliği private yapıp sarmalanmış değeri açık bir hesaplanmış özellik aracılığıyla açmak ve projekte değeri kısıtlı tutmaktır.

Tek bir özellik sarmalayıcı birden fazla ayrı projeksiyon açığa çıkarabilir mi ve ergonomik sonuçları nelerdir?

Swift, her sarmalayıcı için yalnızca bir projectedValue özelliğine katı bir şekilde izin vermektedir. Ancak, o özellik birden fazla değer içeren bir demet, yapı veya enum döndürebilir (örneğin, projectedValue: (Binding<T>, ValidationError?, Bool)). Ergonomik ticaret, $property'nin ardından nokta sözdizimi gerektirmesidir ($property.0, $property.isValid), bu da okunabilirliği azaltır. Bazı adaylar birden fazla projectedValue özelliği tanımlamaya veya aynı özelliğe birden fazla özellik sarmalayıcı uygulamaya (zincirleme) çalışırlar. Zincirleme desteklenirken karmaşık başlatma sözdizimi ve belirsiz tür çıkarım sorunları yaratır. Birden fazla projeksiyon için önerilen yaklaşım, özel adlandırılmış özellikleri olan bir projeksiyon yapısının döndürülmesidir; bu, tür güvenliğini korurken sözdizimi aşamasını kabul etmektedir.