Derin kopya ve sığ kopya - ve bunları Swift'de nasıl kullanabilirsiniz?

Bir nesnenin kopyalanması, kodlama paradigmasının her zaman önemli bir parçası olmuştur. Swift, Objective-C, JAVA veya başka bir dilde olsun, farklı bağlamlarda kullanmak için her zaman bir nesneyi kopyalamamız gerekecek.

Bu makalede, Swift'de farklı veri türlerinin nasıl kopyalanacağını ve farklı koşullarda nasıl davrandıklarını ayrıntılı olarak tartışacağız.

Değer ve Referans türleri

Swift'deki tüm veri türleri genel olarak değer türleri ve başvuru türleri olmak üzere iki kategoriye ayrılır .

  • Değer türü - her örnek, verilerinin benzersiz bir kopyasını tutar. Bu kategoriye giren veri türleri şunları içerir - all the basic data types, struct, enum, array, tuples.
  • Başvuru türü - örnekler, verilerin tek bir kopyasını paylaşır ve tür genellikle bir class.

Her iki türün en ayırt edici özelliği, kopyalama davranışlarında yatmaktadır.

Derin ve Sığ kopya nedir?

İster değer türü ister başvuru türü olsun, bir örnek aşağıdaki yollardan biriyle kopyalanabilir:

Derin kopya - Her şeyi kopyalar

  • Derin bir kopya ile, kaynak tarafından işaret edilen herhangi bir nesne kopyalanır ve kopya, hedef tarafından işaret edilir. Böylece tamamen ayrı iki nesne oluşturulacak.
  • Koleksiyonlar - Bir koleksiyonun derin bir kopyası, orijinal koleksiyondaki tüm öğelerin çoğaltıldığı iki koleksiyondur.
  • Yarış koşullarına daha az eğilimlidir ve çok iş parçacıklı bir ortamda iyi performans gösterir - bir nesnedeki değişikliklerin başka bir nesne üzerinde hiçbir etkisi olmayacaktır.
  • Değer türleri derinlemesine kopyalanır.

Yukarıdaki kodda,

  • Satır 1 : arr1- Dizelerin dizisi (bir değer türü)
  • Satır 2 : arr1atanır arr2. Bu, derin bir kopyasını oluşturur arr1ve ardından bu kopyayıarr2
  • 7. satırdan 11. satıra kadar : içinde yapılan herhangi bir değişiklik arr2yansıtılmaz arr1.

Derin kopya budur - tamamen ayrı örnekler. Aynı konsept, tüm değer türleriyle çalışır.

Bazı senaryolarda, yani bir değer türü iç içe geçmiş başvuru türleri içerdiğinde, derin kopya farklı bir davranış türü ortaya çıkarır. Bunu ilerleyen bölümlerde göreceğiz.

Sığ kopya - Mümkün olduğunca az kopyalar

  • Sığ bir kopya ile, kaynak tarafından işaret edilen herhangi bir nesne, hedef tarafından da işaret edilir. Yani bellekte sadece bir nesne oluşturulacaktır.
  • Koleksiyonlar - Bir koleksiyonun basit bir kopyası, koleksiyon yapısının bir kopyasıdır, öğelerin değil. Yüzeysel bir kopya ile, iki koleksiyon artık ayrı öğeleri paylaşıyor.
  • Daha hızlı - yalnızca referans kopyalanır.
  • Kopyalama referans tiplerini basit bir kopyasını oluşturur.

Yukarıdaki kodda,

  • 1'den 8'e kadar olan satırlar : Addresssınıf türü
  • Satır 10 : a1- örneğidir Addresstürü
  • Satır 11 : a1atanmıştır a2. Bu, basit bir kopyasını oluşturacak a1ve ardından bu kopyayı atayacaktır a2, yani yalnızca referans kopyalanır a2.
  • 16. ve 19. satırlar : içinde yapılan herhangi bir değişiklik a2kesinlikle yansıtılacaktır a1.

Yukarıdaki örnekte, görebiliriz de o a1ve a2aynı hafıza adresine alanına.

Referans Tiplerini Derinlemesine Kopyalama

Şu an itibariyle, bir referans türünü kopyalamaya çalıştığımızda, yalnızca nesneye yapılan referansın kopyalandığını biliyoruz. Yeni nesne oluşturulmaz. Ya tamamen ayrı bir nesne yaratmak istiyorsak?

copy()Yöntemi kullanarak referans türünün derin bir kopyasını oluşturabiliriz . Belgelere göre,

copy () - tarafından döndürülen nesneyi döndürür copy(with:).

Bu, NSCopyingprotokolü benimseyen sınıflar için uygun bir yöntemdir . İçin uygulama yoksa bir istisna ortaya çıkar copy(with:).

Address classCode Snippet 2'de oluşturduğumuz NSCopyingprotokole uyacak şekilde yeniden yapılandıralım .

Yukarıdaki kodda,

  • 1'den 14'e kadar olan satırlar : Addresssınıf türü yönteme uyar NSCopyingve copy(with:)yöntemi uygular
  • Hat 16 : a1- örneğidir Addresstürü
  • Satır 17 : yöntem kullanılarak a1atanır . Bu, derin bir kopyasını oluşturacak ve daha sonra bu kopyayı atayacaktır , yani tamamen yeni bir nesne oluşturulacaktır.a2copy()a1a2
  • 22 - 25. satırlar : içinde yapılan herhangi bir değişiklik içeri a2yansıtılmayacaktır a1.

Gibi, yukarıdaki şekilden hem belirgindir a1ve a2farklı bellek konumlarına işaret etmektedir.

Başka bir örneğe bakalım. Bu sefer , başka bir referans türünü içeren bir referans türü olan iç içe geçmiş referans türleriyle nasıl çalıştığını göreceğiz .

Yukarıdaki kodda,

  • Satır 22: yöntem kullanılarak derin bir kopyası p1atanır . Bu, birindeki herhangi bir değişikliğin diğerinde herhangi bir etkisinin olmaması gerektiği anlamına gelir.p2copy()
  • 27 ile 28 arasındaki satırlar:p2’sname ve citydeğerler değiştirilir. Bunlar yansıtılmamalıdır p1.
  • Satır 30:p1’sname beklendiği gibi, ama city? O olmalı “Mumbai”değil ki? Ama bunun olduğunu göremiyoruz. “Bangalore”sadece p2doğru muydu? Evet… tam olarak.?

Derin kopya…! ? T şapka senden beklenmiyordu. Her şeyi kopyalayacağını söyledin. Ve şimdi böyle davranıyorsun. Neden oh neden ..?! Ben şimdi ne yapacağım? ☠

Panik yapmayın. Burada hafıza adreslerinin ne söyleyeceğine bakalım.

Yukarıdaki çizimden bunu görebiliriz

  • p1ve p2beklendiği gibi farklı bellek konumlarına işaret edin.
  • Ancak addressdeğişkenleri hala aynı yere işaret ediyor. Bu, derinlemesine kopyalandıktan sonra bile, yalnızca referansların kopyalandığı anlamına gelir - yani, elbette sığ bir kopya .

Lütfen dikkat: Bir referans türünü her kopyaladığımızda, derinlemesine kopyalanması gerektiğini açıkça belirtene kadar varsayılan olarak sığ bir kopya oluşturulur.

func copy(with zone: NSZone? = nil) -> Any{ let person = Person(self.name, self.address) return person}

PersonSınıf için daha önce uyguladığımız yukarıdaki yöntemde , adresi ile kopyalayarak yeni bir örnek oluşturduk self.address. Bu yalnızca referansı adres nesnesine kopyalar. Bu sebebi hem p1ve p2’saddressaynı konuma gelin.

Bu nedenle, copy()yöntemi kullanarak nesneyi kopyalamak, nesnenin gerçek bir derin kopyasını oluşturmaz .

Bir referans nesnesini tamamen çoğaltmak için: tüm iç içe geçmiş referans türleriyle birlikte referans türü yöntemle kopyalanmalıdır copy().

let person = Person(self.name, self.address.copy() as? Address)

Yukarıdaki kodu func copy(with zone: NSZone? = nil) ->Any yönteminde kullanmak her şeyin çalışmasını sağlayacaktır. Bunu aşağıdaki resimden görebilirsiniz.

Gerçek Derin Kopya - Referans ve Değer türleri

Referans türlerinin nasıl derin bir kopyasını oluşturabileceğimizi zaten gördük. Elbette bunu tüm iç içe geçmiş referans türleri ile yapabiliriz.

Ancak bir değer türündeki iç içe geçmiş başvuru türü, yani bir nesneler dizisi veya bir yapıdaki veya belki bir demetteki başvuru türü değişkeni ne olacak? Bunu da kullanarak çözebilir miyiz copy()? Hayır, aslında yapamayız. copy()Yöntem uygulamak gerekir NSCopyingsadece çalışır protokolü NSObjectalt sınıfları. Değer türleri mirası desteklemez, bu yüzden copy()onlarla kullanamayız .

2. satırda, yalnızca yapısı arr1derinlemesine kopyalanır, ancak içindeki Addressnesneler hala sığ kopyalanır. Bunu aşağıdaki hafıza haritasından görebilirsiniz.

Her ikisindeki arr1ve arr2her ikisindeki öğeler aynı bellek konumlarına işaret ediyor. Bunun nedeni aynı nedendir - referans türleri varsayılan olarak yüzeysel olarak kopyalanır.

Bir nesnenin serileştirilmesi ve ardından serileştirilmesi her zaman yepyeni bir nesne oluşturur. Hem değer türleri hem de referans türleri için geçerlidir.

Verileri serileştirmek ve serileştirmeyi kaldırmak için kullanabileceğimiz bazı API'ler şunlardır:

  1. NSCoding - Arşivleme ve dağıtım için bir nesnenin kodlanmasını ve kodunun çözülmesini sağlayan bir protokol. classDevralmayı gerektirdiği için yalnızca yazım nesneleriyle çalışır NSObject.
  2. Kodlanabilir - Veri türlerinizi JSON gibi harici temsillerle uyumluluk için kodlanabilir ve kodu çözülebilir hale getirin. Her iki değer türü için de - struct, array, tuple, basic data typesreferans türleri için - çalışacaktır class.

Protokole Addressuymak için sınıfı biraz daha yeniden yapılandıralım ve daha önce Code Snippet 3'te eklediğimiz Codabletüm NSCopyingkodu kaldıralım.

Yukarıdaki kodda, 11-13. Satırlar öğesinin gerçek bir derin kopyasını oluşturacaktır arr1. Aşağıda, bellek konumlarının net bir resmini veren resim bulunmaktadır.

Yazarken Kopyala

Yazma üzerine kopyalama, değer türlerini kopyalarken performansı artırmaya yardımcı olan bir optimizasyon tekniğidir.

Diyelim ki tek bir String veya Int veya başka bir değer türü kopyaladığımızı varsayalım - bu durumda önemli performans sorunlarıyla karşılaşmayacağız. Peki ya binlerce öğe dizisini kopyaladığımızda? Yine de herhangi bir performans sorunu yaratmayacak mı? Ya sadece kopyalarsak ve o kopyada herhangi bir değişiklik yapmazsak? Bu durumda kullandığımız fazladan hafıza boşa gitmiyor mu?

İşte Yazmada Kopyala kavramı geliyor - kopyalarken, her referans aynı hafıza adresine işaret ediyor. Sadece referanslardan biri temeldeki verileri değiştirdiğinde, Swift orijinal örneği gerçekten kopyalar ve değişikliği yapar.

Yani ister derin kopya ister sığ kopya olsun, nesnelerden birinde değişiklik yapana kadar yeni bir kopya oluşturulmayacaktır.

Yukarıdaki kodda,

  • Satır 2 : arr1öğesinin derin bir kopyası atanırarr2
  • 4. ve 5. satırlar : arr1ve arr2yine de aynı hafıza adresini gösteriyor
  • Satır 7 : yapılan değişikliklerarr2
  • Satır 9 ve 10 : arr1ve arr2şimdi farklı bellek konumlarına işaret ediyor

Artık derin ve sığ kopyalar ve farklı veri türleriyle farklı senaryolarda nasıl davrandıkları hakkında daha fazla şey biliyorsunuz. Bunları kendi örneklerinizle deneyebilir ve hangi sonuçları aldığınızı görebilirsiniz.

daha fazla okuma

Diğer makalelerimi okumayı unutma:

  1. Swift 4'te Codable hakkında her şey
  2. İOS'taki bildirimler hakkında her zaman bilmek istediğiniz her şey
  3. GRADIENTS ile renklendirin - iOS
  4. İOS 11 için Kodlama: Koleksiyonlara ve tablolara nasıl sürükleyip bırakılır
  5. İOS 10'daki Today Extensions (Widget) hakkında bilmeniz gereken her şey
  6. UICollectionViewCell seçimi artık çok kolay .. !!

Herhangi bir sorunuz olması durumunda yorum bırakmaktan çekinmeyin.