Swift, değer türü enumların indirect anahtar kelimesini kullanarak sonsuz özyineleme sağlamasına olanak tanır; bu, belirli durumların ilişkili değerlerini yığın tahsisli, referans sayımlı kutular içinde saklamasını zorunlu kılar. Bir durum indirect olarak işaretlendiğinde, derleyici, satır içi yük tanımlama depolamasını, ARC tarafından yönetilen yığın tahsisli bir konteynere işaretçi olarak dönüştürür. Bu dolaylılık, enum'un kendisini sonsuz boyut genişlemesi olmadan özyinelemeli olarak referans almasına olanak tanır; çünkü derleyici yalnızca tam değeri satır içinde depolamak yerine bir işaretçi saklamak zorundadır.
Ancak bu dönüşüm, desen eşleştirme performansını önemli ölçüde etkiler. Her indirect durumuna erişim, yükü almak için işaretçi takibi gerektirir; bu, yığında tamamen depolanmış enumlara kıyasla CPU önbellek yerelliğini düşürür. Ayrıca, yığın tahsisi, eşzamanlı bağlamlarda senkronizasyon yükünü artıran atomik tutma ve bırakma işlemleri getirir, ancak enum'un kendisi dil seviyesinde değer anlamını korur.
indirect enum Expression { case literal(Int) case add(Expression, Expression) case multiply(Expression, Expression) } // Desen eşleştirme, referansın çözülmesini gerektirir func evaluate(_ expr: Expression) -> Int { switch expr { case .literal(let value): return value case .add(let left, let right): return evaluate(left) + evaluate(right) case .multiply(let left, let right): return evaluate(left) * evaluate(right) } }
Bir yapılandırma motoru için derin yerleştirilmiş mantıksal ifadeleri işlemekten oluşan bir alan spesifik dil ayrıştırıcısı geliştirirken, indirect notasyonları olmadan ifadeyi temsil etmek için özyinelemeli bir enum kullandık. Bu, birkaç bin seviyeden daha fazla derinliğe sahip yapılandırma dosyalarını işlerken hemen yığın taşması hatalarını tetikledi.
İlk olarak düşünülen çözüm, tam anlamıyla yığın tahsisli ağaç yapısı ile birlikte ebeveyn ve çocuk referansları kullanarak enumları tamamen terk etmekti. Bu yaklaşım, özyinelemeli ilişkiler için doğal bir yığın tahsisi sunacaktı. Ancak, değer anlamını feda ettiği için, karmaşık savunmaya dayanan kopyalama veya kilitleme mekanizmaları uygulamadan ayrıştırılmış alt ağaçları eşzamanlı derleme iplikleri arasında güvenli bir şekilde paylaşmayı imkansız kıldığı için bu durumu reddettik.
İkinci çözümü seçtik: Enum'daki çocuk ifadelerini içeren özyinelemeli durumlara indirect uygulamak. Bu, değer anlamını korurken, yalnızca gerekli olan yerlerde yığın tahsisini zorunlu kıldı. Bu değişim, değişmezlik garantileri ve tür güvenliği sağlamaya devam ettiğimiz için kabul edilebilirdi, ancak sık sık değiştirilen ifade ağaçları için özel kopyalama gerçekleştirirken bazı optimizasyonlar implement etmemiz gerekti.
Sonuç, her türlü derin yerleşimi işleyebilen kararlı bir ayrıştırıcı oldu. Profiling daha sonra indirect durumlarında desen eşleştirmesinin işaretçi dolaylılığı ve ARC trafiği nedeniyle yaklaşık yirmi yüzde daha fazla CPU döngüsü tükettiğini ortaya çıkardı; bunu sık sık kullanılan durumlar için küçük sabit derinlik yapılarının dolaylı olmayan yardımcı enum'lara düzleştirilmesiyle hafiflettik.
indirect Swift'in kopyala-yaz bilgisini nasıl etkiliyor?
Birçok aday, indirect durumlarının her zaman tüm özyinelemeli yapının derin kopyalanmasını tetiklediğini varsayıyor. Gerçekte, Swift, dolaylı yükü içeren yığın kutusuna kopyalama yaptığında, yalnızca yığın kutusunun referansını saklar; içeriği kopyalamaz. Yük, yalnızca bir değiştirici işlem gerçekleştiğinde ve referans sayımı birden fazla olduğunda kopyalanır. Bu optimizasyon, büyük özyinelemeli yapılar için performans açısından kritik öneme sahiptir; ancak, referans sayımının atomik olmasına rağmen, kopyala-yaz mantığı iplikler arasında senkronizasyon gerektirir.
indirect sadece bireysel durumlara uygulanabilir mi ve bellek düzeni üzerindeki etkileri nelerdir?
Adaylar genellikle indirect'in tüm enum bildirimine uygulanması gerektiğini düşünür. Ancak, Swift, bireysel durumların indirect olarak işaretlenmesine izin verir; bu, bellek düzenini önemli ölçüde etkiler. Belirli durumlar indirect olarak işaretlendiğinde, enum, dolaylı durumların yığın kutusuna işaret eden bir kelime boyutunda gösterim kullanır; dolaylı olmayan durumlar ise yüklerini enum'un bellek ayak izine satır içinde saklar. Bu karışık temsil, yalnızca belirli durumların özyineleme gerektirdiği enumlar için bellek kullanımını optimize eder. Ancak, deseni eşleştirme karmaşıklığını artırır çünkü derleyici, satır içi ile dolaylı yükler için farklı erişim kodu yolları üretmek zorundadır ve enum'un genel boyutu, en büyük satır içi yük artı etiket bitleri tarafından belirlenir, dolaylı durumların boyutları değil.
indirect ile özyinelemeli enumlar, kapanışlar söz konusu olduğunda neden referans döngüleri oluşturabilir ve bu durum standart değer türü davranışından nasıl farklıdır?
Bu, ARC hakkında derin bir anlayış gerektiren ince bir noktadır. Normalde, enumlar gibi değer türleri kimlik ve referans sayımı eksik olduğundan referans döngüleri oluşturamaz. Ancak, bir durum indirect olarak işaretlendiğinde, yük yığın tahsisli ve referans sayımlı hale gelir. Bir indirect durumunun ilişkili değerleri, enum'un kendisini yakalayan bir kapanış içerirse ve bu kapanış, enum'un ilişkili değerlerine geri konulursa, bir döngü oluşur. Bu, döngünün yığın tahsisli kutuda bulunması dışında, sınıf tabanlı döngülerden farklıdır; enum değeri değil. Döngüyü kırmak için, [weak self] veya [unowned self] gibi yakalama listeleri kullanmak zorundasınız, ancak genellikle enumlar değer türü olduğu için, geliştiriciler, indirect'in yük için referans anlamı getirdiğini unutarak kapanışlarla çalışırken aynı dikkat gerektirdiğini unutur.