
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 :
arr1
atanırarr2
. Bu, derin bir kopyasını oluştururarr1
ve ardından bu kopyayıarr2
- 7. satırdan 11. satıra kadar : içinde yapılan herhangi bir değişiklik
arr2
yansıtılmazarr1
.
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 :
Address
sınıf türü - Satır 10 :
a1
- örneğidirAddress
türü - Satır 11 :
a1
atanmıştıra2
. Bu, basit bir kopyasını oluşturacaka1
ve ardından bu kopyayı atayacaktıra2
, yani yalnızca referans kopyalanıra2
. - 16. ve 19. satırlar : içinde yapılan herhangi bir değişiklik
a2
kesinlikle yansıtılacaktıra1
.

Yukarıdaki örnekte, görebiliriz de o a1
ve a2
aynı 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, NSCopying
protokolü benimseyen sınıflar için uygun bir yöntemdir . İçin uygulama yoksa bir istisna ortaya çıkar copy(with:)
.
Address class
Code Snippet 2'de oluşturduğumuz NSCopying
protokole uyacak şekilde yeniden yapılandıralım .
Yukarıdaki kodda,
- 1'den 14'e kadar olan satırlar :
Address
sınıf türü yönteme uyarNSCopying
vecopy(with:)
yöntemi uygular - Hat 16 :
a1
- örneğidirAddress
türü - Satır 17 : yöntem kullanılarak
a1
atanır . Bu, derin bir kopyasını oluşturacak ve daha sonra bu kopyayı atayacaktır , yani tamamen yeni bir nesne oluşturulacaktır.a2
copy()
a1
a2
- 22 - 25. satırlar : içinde yapılan herhangi bir değişiklik içeri
a2
yansıtılmayacaktıra1
.

Gibi, yukarıdaki şekilden hem belirgindir a1
ve a2
farklı 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ı
p1
atanır . Bu, birindeki herhangi bir değişikliğin diğerinde herhangi bir etkisinin olmaması gerektiği anlamına gelir.p2
copy()
- 27 ile 28 arasındaki satırlar:
p2’s
name
vecity
değerler değiştirilir. Bunlar yansıtılmamalıdırp1
. - Satır 30:
p1’s
name
beklendiği gibi, amacity
? O olmalı“Mumbai”
değil ki? Ama bunun olduğunu göremiyoruz.“Bangalore”
sadecep2
doğ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
p1
vep2
beklendiği gibi farklı bellek konumlarına işaret edin.- Ancak
address
değ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}
Person
Sı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 p1
ve p2’s
address
aynı 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 NSCopying
sadece çalışır protokolü NSObject
alt sınıfları. Değer türleri mirası desteklemez, bu yüzden copy()
onlarla kullanamayız .
2. satırda, yalnızca yapısı arr1
derinlemesine kopyalanır, ancak içindeki Address
nesneler hala sığ kopyalanır. Bunu aşağıdaki hafıza haritasından görebilirsiniz.

Her ikisindeki arr1
ve arr2
her 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:
- NSCoding - Arşivleme ve dağıtım için bir nesnenin kodlanmasını ve kodunun çözülmesini sağlayan bir protokol.
class
Devralmayı gerektirdiği için yalnızca yazım nesneleriyle çalışırNSObject
. - 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 types
referans türleri için - çalışacaktırclass
.
Protokole Address
uymak için sınıfı biraz daha yeniden yapılandıralım ve daha önce Code Snippet 3'te eklediğimiz Codable
tüm NSCopying
kodu 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 :
arr1
vearr2
yine de aynı hafıza adresini gösteriyor - Satır 7 : yapılan değişiklikler
arr2
- Satır 9 ve 10 :
arr1
vearr2
ş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:
- Swift 4'te Codable hakkında her şey
- İOS'taki bildirimler hakkında her zaman bilmek istediğiniz her şey
- GRADIENTS ile renklendirin - iOS
- İOS 11 için Kodlama: Koleksiyonlara ve tablolara nasıl sürükleyip bırakılır
- İOS 10'daki Today Extensions (Widget) hakkında bilmeniz gereken her şey
- UICollectionViewCell seçimi artık çok kolay .. !!
Herhangi bir sorunuz olması durumunda yorum bırakmaktan çekinmeyin.