web-dev-qa-db-ja.com

NSArrayのディープコピー

NSMutableArrayをディープコピーできる組み込み関数はありますか?

私は周りを見回したが、一部の人々は[aMutableArray copyWithZone:nil]はディープコピーとして機能します。しかし、私は試しましたが、浅いコピーのようです。

現在、forループを使用して手動でコピーを実行しています。

//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];
            }
        }
    }
}

しかし、より簡潔で簡潔なソリューションが欲しいです。

116
ivanTheTerrible

Apple docs 状態として、

1レベルの深さのコピーのみが必要な場合は、1つを明示的に呼び出すことができます...

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

上記のコードは、メンバーが古い配列のメンバーの浅いコピーである新しい配列を作成します。

ネストされたデータ構造全体を深くコピーする必要がある場合-リンクされたApple docs calls "true deep copy"-このアプローチでは十分ではありません-他の回答を参照してください。

206
François P.

これを簡単に行える唯一の方法は、アレイをアーカイブしてからすぐにアーカイブ解除することです。ちょっとしたハックのように感じますが、実際には コレクションのコピーに関するAppleドキュメント で明示的に提案されています。

配列の配列がある場合など、真のディープコピーが必要な場合は、コンテンツがすべてNSCodingプロトコルに準拠していれば、コレクションをアーカイブしてからアーカイブ解除できます。この手法の例をリスト3に示します。

リスト3真のディープコピー

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

キャッチは、オブジェクトがNSCodingインターフェースをサポートする必要があることです。これは、データの保存/ロードに使用されるためです。

Swift 2バージョン:

let trueDeepCopyArray = NSKeyedUnarchiver.unarchiveObjectWithData(
    NSKeyedArchiver.archivedDataWithRootObject(oldArray))
59
Andrew Grant

デフォルトでコピーは浅いコピーを提供します

これは、copyの呼び出しがcopyWithZone:NULLと同じであり、デフォルトゾーンでのコピーとしても知られているためです。 copy呼び出しでは、ディープコピーは行われません。ほとんどの場合、それは浅いコピーを提供しますが、いずれにしてもクラスに依存します。徹底的な議論のために、Apple Developerサイトで Collections Programming Topics をお勧めします。

initWithArray:CopyItems:1レベルのディープコピーを提供します

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

NSCodingはAppleディープコピーを提供する推奨方法です

真のディープコピー(配列の配列)には、NSCodingとオブジェクトのアーカイブ/アーカイブ解除が必要です。

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

ディクトン用

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

配列の場合

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

7
Darshit Shah

いいえ、このためのフレームワークには何も組み込まれていません。 Cocoaコレクションは、浅いコピーをサポートします(copyまたはarrayWithArray:メソッド)が、ディープコピーの概念については説明しません。

これは、独自のカスタムオブジェクトを含むコレクションのコンテンツが開始されると、「ディープコピー」の定義が困難になるためです。 「ディープコピー」とは、オブジェクトグラフ内のeveryオブジェクトがevery元のオブジェクトグラフのオブジェクト?

仮想NSDeepCopyingプロトコルがある場合、これを設定してすべてのオブジェクトで決定を下すことができますが、残念ながらありません。グラフ内のほとんどのオブジェクトを制御した場合、このプロトコルを自分で作成して実装できますが、必要に応じてFoundationクラスにカテゴリを追加する必要があります。

キー付きアーカイブ/アーカイブ解除の使用を示唆する@A​​ndrewGrantの回答は、パフォーマンスは低下しますが、任意のオブジェクトに対してこれを達成するための正しいクリーンな方法です。 この本はここまで進んでいますので、提案してください ディープコピーをサポートするために正確にそれを行うすべてのオブジェクトにカテゴリを追加します。

4
Ben Zotto

JSON互換データのディープコピーを達成しようとすると、回避策があります。

NSDataを使用してNSArrayNSJSONSerializationを取得し、JSONオブジェクトを再作成するだけで、NSArray/NSDictionaryそれらの新しいメモリ参照。

ただし、NSArray/NSDictionaryのオブジェクトとその子はJSONシリアル化可能でなければなりません。

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