NSArray'i derinlemesine kopyalama


119

Derin kopyalama yapmama izin veren herhangi bir yerleşik işlev var NSMutableArraymı?

Etrafıma baktım, bazıları diyor ki [aMutableArray copyWithZone:nil] eserlerin derin kopya . Ama denedim ve sığ bir kopya gibi görünüyor.

Şu anda kopyayı bir fordöngü ile manuel olarak yapıyorum :

//deep copy a 9*9 mutable array to a passed-in reference array

-deepMuCopy : (NSMutableArray*) array 
    toNewArray : (NSMutableArray*) arrayNew {

    [arrayNew removeAllObjects];//ensure it's clean

    for (int y = 0; y<9; y++) {
        [arrayNew addObject:[NSMutableArray new]];
        for (int x = 0; x<9; x++) {
            [[arrayNew objectAtIndex:y] addObject:[NSMutableArray new]];

            NSMutableArray *aDomain = [[array objectAtIndex:y] objectAtIndex:x];
            for (int i = 0; i<[aDomain count]; i++) {

                //copy object by object
                NSNumber* n = [NSNumber numberWithInt:[[aDomain objectAtIndex:i] intValue]];
                [[[arrayNew objectAtIndex:y] objectAtIndex:x] addObject:n];
            }
        }
    }
}

ama daha temiz, daha özlü bir çözüm istiyorum.


44
@Genericrich derin ve sığ kopyalar, yazılım geliştirmede oldukça iyi tanımlanmış terimlerdir. Google.com yardımcı olabilir
Andrew Grant

1
Belki de kafa karışıklığının bir kısmı -copy, değişmez koleksiyonlardaki davranışının Mac OS X 10.4 ve 10.5 arasında değişmesidir: developer.apple.com/library/mac/releasenotes/Cocoa/… ("Değişmez koleksiyonlar ve kopyalama davranışı" na gidin)
user102008

1
@AndrewGrant Daha fazla düşündüğümde ve saygıyla, derin kopyanın iyi tanımlanmış bir terim olduğuna katılmıyorum . Hangi kaynağı okuduğunuza bağlı olarak, iç içe geçmiş veri yapılarına sınırsız özyinelemenin bir 'derin kopyalama' işleminin bir gerekliliği olup olmadığı açık değildir. Diğer bir deyişle, üyeleri orijinal nesnenin basit kopyaları olan yeni bir nesneyi oluşturan bir kopyalama işleminin 'derin kopyalama' işlemi olup olmadığı konusunda çelişkili yanıtlar alacaksınız. Bununla ilgili bazı tartışma için stackoverflow.com/a/6183597/1709587 adresine bakın (Java bağlamında, ancak hepsi aynı şekilde ilgilidir).
Mark Amery

@AndrewGrant @MarkAmery ve @Genericrich yedeklemem gerekiyor. Bir koleksiyonda kullanılan kök sınıf ve tüm öğeleri kopyalanabilirse, derin bir kopya iyi tanımlanır. NSArray (ve diğer objc koleksiyonlarında) durum böyle değildir. Bir öğe uygulanmazsa copy, "derin kopyaya" ne konulmalıdır? Öğe başka bir koleksiyonsa, copyaslında (aynı sınıfın) bir kopyasını vermez. Bu nedenle, belirli bir durumda istenen kopyanın türü hakkında tartışmanın tamamen geçerli olduğunu düşünüyorum.
Nikolai Ruhe

@NikolaiRuhe Eğer bir eleman NSCopying/ uygulamazsa -copy, o zaman kopyalanamaz - bu yüzden asla onun bir kopyasını yapmaya çalışmamalısınız, çünkü bu sahip olması için tasarlanmış bir yetenek değildir. Cocoa'nın uygulanması açısından, kopyalanamayan nesneler genellikle bağlı oldukları bazı C arka uç durumuna sahiptir, bu nedenle nesnenin doğrudan bir kopyasını kesmek yarış koşullarına veya daha kötüsüne yol açabilir. Öyleyse, “'derin kopyaya ne konulacak” yanıtını vermek için - A tutulan bir referans. NSCopyingNesne olmayan bir şeyiniz olduğunda her yere koyabileceğiniz tek şey .
Slipp D. Thompson

Yanıtlar:


210

Gibi derin kopya konusunda Apple belgelerine açıkça belirtmektedir:

Yalnızca tek düzeyli derin bir kopyaya ihtiyacınız varsa :

NSMutableArray *newArray = [[NSMutableArray alloc] 
                             initWithArray:oldArray copyItems:YES];

Yukarıdaki kod, üyeleri eski dizinin üyelerinin sığ kopyaları olan yeni bir dizi oluşturur.

Bağlı Apple belgelerinin gerçek derin kopya dediği iç içe geçmiş bir veri yapısının tamamını derinlemesine kopyalamanız gerekiyorsa, bu yaklaşımın yeterli olmayacağını unutmayın. Lütfen bunun için buradaki diğer cevaplara bakın.


Doğru cevap gibi görünüyor. API, her öğenin bir [element copyWithZone:] mesajı aldığını belirtir, bu sizin gördüğünüz şey olabilir. Aslında [NSMutableArray copyWithZone: nil] gönderiminin derinlemesine kopyalama olmadığını görüyorsanız, bir dizi dizisi bu yöntemi kullanarak doğru şekilde kopyalanmayabilir.
Ed Marty

7
Bunun beklendiği gibi çalışacağını sanmıyorum. Apple dokümanlarından: "copyWithZone: yöntemi yüzeysel bir kopya gerçekleştirir . Eğer keyfi bir derinlik koleksiyonunuz varsa, bayrak parametresi için YES'i geçmek, yüzeyin altındaki ilk seviyenin değişmez bir kopyasını gerçekleştirir. Eğer HAYIR'ı geçerseniz değişkenliği ilk seviye etkilenmez. Her iki durumda da, daha derin tüm seviyelerin değişkenliği etkilenmez. "SO sorusu derin değişken kopyalarla ilgilidir.
Joe D'Andrea

7
Bu derin bir kopya DEĞİL
Daij-Djan

9
Bu eksik bir cevaptır. Bu, tek düzeyli derin bir kopya ile sonuçlanır. Dizi içinde daha karmaşık türler varsa, derin bir kopya sağlamaz.
Cameron Lowell Palmer

7
Bu , alıcı sınıfa nasıl uygulandığına bağlı olarak derin bir kopya olabilircopyWithZone: .
devios1

62

Bunu kolayca yapmanın tek yolu arşivlemek ve ardından dizinizi hemen arşivden çıkarmaktır. Biraz hack gibi geliyor ama aslında koleksiyonları kopyalamayla ilgili Apple Dokümantasyonunda açıkça öneriliyor :

Bir dizi diziniz olduğunda olduğu gibi gerçek bir derin kopyaya ihtiyacınız varsa, tüm içeriğin NSCoding protokolüne uygun olması koşuluyla koleksiyonu arşivleyebilir ve ardından arşivden çıkarabilirsiniz. Bu tekniğin bir örneği Liste 3'te gösterilmektedir.

Liste 3 Gerçek bir derin kopya

NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
          [NSKeyedArchiver archivedDataWithRootObject:oldArray]];

Buradaki sorun, nesnenizin NSCoding arayüzünü desteklemesi gerektiğidir, çünkü bu, verileri depolamak / yüklemek için kullanılacaktır.

Swift 2 Sürümü:

let trueDeepCopyArray = NSKeyedUnarchiver.unarchiveObjectWithData(
    NSKeyedArchiver.archivedDataWithRootObject(oldArray))

12
NSArchiver ve NSUnarchiver kullanmak, dizileriniz büyükse performans açısından çok ağır bir çözümdür. NSCopying protokolünü kullanan genel bir NSArray kategori yöntemi yazmak işe yarayacak ve değişmez nesnelerin basit bir "tutulmasına" ve değiştirilebilir nesnelerin gerçek bir "kopyasına" neden olacaktır.
Nikita Zhuk

Masraf konusunda ihtiyatlı olmak iyidir, ancak NSCoding, yöntemde kullanılan NSCopying'den gerçekten daha pahalı mıdır initWithArray:copyItems:? Bu arşivleme / arşivden çıkarma geçici çözümü, kaç kontrol sınıfının NSCoding'e uyup NSCopying'e uymadığı düşünüldüğünde çok yararlı görünmektedir.
Wienke

Bu yaklaşımı asla kullanmamanızı şiddetle tavsiye ederim. Serileştirme hiçbir zaman hafızayı kopyalamaktan daha hızlı değildir.
Brett

1
Özel nesneleriniz varsa, NSCoding protokolüne uymak için encodeWithCoder ve initWithCoder'ı uyguladığınızdan emin olun.
user523234

Açıkçası, serileştirmeyi kullanırken programcının takdirine bağlı olarak şiddetle tavsiye edilir.
VH-NZZ

34

Varsayılan olarak kopyala sığ bir kopya verir

Bunun nedeni, çağrının varsayılan bölge ile kopyalama copyolarak copyWithZone:NULLda bilinenle aynı olmasıdır . copyÇağrı derin kopya yol açmaz. Çoğu durumda size sığ bir kopya verir, ancak her durumda sınıfa bağlıdır. Kapsamlı bir tartışma için Apple Developer sitesindeki Koleksiyon Programlama Konularını tavsiye ederim .

initWithArray: CopyItems: tek düzeyli derin bir kopya verir

NSArray *deepCopyArray = [[NSArray alloc] initWithArray:someArray copyItems:YES];

NSCoding derin bir kopya sağlamanın Apple tarafından önerilen yolu

Gerçek bir derin kopya (Array of Arrays) NSCodingiçin nesneye ihtiyacınız olacak ve arşivlemeniz / arşivden çıkarmanız gerekir :

NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

1
Doğru. Bellek, performans ile ilgili popülerlik veya zamanından önce optimize edilmiş uç durumlar ne olursa olsun. Bir kenara, derin kopya için bu ser-derser hacklemesi diğer birçok dil ortamında kullanılmaktadır. Nesne tekilleştirme olmadığı sürece, bu orijinalden tamamen ayrı, iyi bir derin kopya garanti eder.

1
İşte daha az döndürülebilir bir Apple doc url geliştiricisi.apple.com

7

Dictonary için

NSMutableDictionary *newCopyDict = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)objDict, kCFPropertyListMutableContainers);

Dizi için

NSMutableArray *myMutableArray = (NSMutableArray *)CFPropertyListCreateDeepCopy(NULL, arrData, kCFPropertyListMutableContainersAndLeaves);


4

Hayır, bunun için çerçevelerde yerleşik bir şey yok. Kakao koleksiyonları sığ kopyaları destekler ( copyveya arrayWithArray:yöntemleriyle) ancak derin bir kopya konseptinden bahsetmez bile.

Bunun nedeni, koleksiyonlarınızın içeriği kendi özel nesnelerinizi dahil etmeye başladıkça "derin kopyanın" tanımlanmasının zorlaşmaya başlamasıdır. "Derin kopya" anlamına mı her nesne grafiğinde obje için benzersiz bir referans görecelidir her orijinal nesne grafiğinde nesne?

Varsayımsal bir NSDeepCopyingprotokol olsaydı , bunu kurabilir ve tüm nesnelerinizde kararlar alabilirsiniz, ancak maalesef yok. Grafiğinizdeki nesnelerin çoğunu kontrol ettiyseniz, bu protokolü kendiniz oluşturabilir ve uygulayabilirsiniz, ancak gerekirse Temel sınıflarına bir kategori eklemeniz gerekir.

@ AndrewGrant'ın anahtarlı arşivleme / arşivden çıkarmanın kullanılmasını öneren yanıtı, bunu keyfi nesneler için gerçekleştirmenin uygun olmayan ancak doğru ve temiz bir yoludur. Bu kitap şimdiye kadar ileri gidiyor, bu nedenle, derin kopyalamayı desteklemek için tam olarak bunu yapan tüm nesnelere bir kategori eklemenizi önerin .


2

JSON uyumlu veriler için derin kopya elde etmeye çalışıyorsam bir çözümüm var.

Basitçe almak NSDatave NSArraykullanma NSJSONSerializationve sonra JSON Nesne yeniden, bu tam bir yeni ve taze bir kopyasını yaratacak NSArray/NSDictionaryonları yeni bellek referanslarla.

Ancak NSArray / NSDictionary nesnelerinin ve bunların alt öğelerinin JSON serileştirilebilir olması gerektiğinden emin olun.

NSData *aDataOfSource = [NSJSONSerialization dataWithJSONObject:oldCopy options:NSJSONWritingPrettyPrinted error:nil];
NSDictionary *aDictNewCopy = [NSJSONSerialization JSONObjectWithData:aDataOfSource options:NSJSONReadingMutableLeaves error:nil];

1
NSJSONReadingMutableContainersBu sorudaki kullanım senaryosunu belirtmelisiniz .
Nikolai Ruhe
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.