12

クラスに辞書の配列を作成しています。その配列のコピーを、それを要求する他のオブジェクトに返したいと思います。他のオブジェクトに渡されるこのコピーは、オリジナルを変更せずに変更する必要があります。

したがって、「マスター」配列を保持するクラスのゲッターメソッドで次を使用しています。

[[NSMutableArray alloc] initWithArray:masterArray copyItems:YES];

ただし、これにより、内部のすべての辞書が不変になるようです。どうすればこれを回避できますか?

ここで何かが足りないと思います。どんな助けでも大歓迎です!

4

4 に答える 4

14

もう 1 つの方法として、CFPropertyListCreateDeepCopy() 関数を (CoreFoundation フレームワークで) 使用し、mutabilityOption 引数に kCFPropertyListMutableContainers を渡す方法があります。コードは次のようになります。

NSMutableArray* originalArray;
NSMutableArray* newArray;

newArray = (NSMutableArray*)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFPropertyListRef)originalArray, kCFPropertyListMutableContainers);

これにより、辞書の変更可能なコピーが作成されるだけでなく、それらの辞書に含まれるあらゆるものの変更可能なコピーも再帰的に作成されます。ただし、これは、辞書の配列に有効なプロパティ リスト (配列、数値、日付、データ、文字列、および辞書) であるオブジェクトのみが含まれている場合にのみ機能するため、特定の状況では適用できない場合があることに注意してください。

于 2009-07-28T20:39:53.963 に答える
6

配列をコピーすると、元のコンテンツ オブジェクトへの参照を含む配列オブジェクトのコピーが作成されます (適切に保持されます)。

ドキュメントに従って、元の配列のアイテムのコピー-initWithArray:copyItems:で新しい配列を初期化します。このコピーは、元のコンテンツ オブジェクトを送信することによって作成されます。変更可能なオブジェクトの場合は、不変のコピーが作成されます。-copyWithZone:

別の動作 (つまり、コンテンツ オブジェクトの変更可能なコピー、またはコンテンツ オブジェクトのディープ コピー) が必要な場合は、そのために独自の便利な関数/メソッドを作成する必要があります。

于 2009-07-28T13:41:46.813 に答える
5

1 つの方法は、キー値コーディングを悪用して、各mutableCopy辞書とautorelease各コピーに送信することです。しかし、それは汚い、汚いハックなので、そうしないでください。実際、そもそもこれを行うべきではないでしょう。

一般的に、「辞書の配列」という言葉を見ると、モデル オブジェクトの代わりに辞書を使用していると思います。そうしないでください。独自のモデル クラスを記述します。独自のカスタム プロパティと動作メソッドを使用すると、すべてがはるかに簡単になります。(他よりも重要なこと: AppleScript サポートの実装は、適切なモデル層なしでは事実上不可能です。)

そして、実際のモデル オブジェクトを取得したら、それらを実装することができ、NSCopying変更可能か不変かを心配する必要はありません。実際のモデル クラスでは変更可能性の区別がおそらくないからです。(他の人については知りませんが、モデル クラスでそのような区別をしたことはありません。) 次に、既存の NSArrayinitWithArray:copyItems:メソッドを使用できます。

于 2009-07-28T19:37:23.887 に答える
2

Jim は、各要素にメッセージを-initWithArray:copyItems送信することについて正しいです。-copyWithZone:配列要素の変更可能なコピーを取得するには、各要素に送信する必要があります-mutableCopyWithZone:(または-mutableCopy簡潔にするために)。これはかなり簡単です。

NSMutableArray *masterArray = ...
NSMutableArray *clone = [NSMutableArray arrayWithCapacity:[masterArray count]];
for (id anObject in masterArray)
    [clone addObject:[anObject mutableCopy]]; // OR [clone addObject:anObject];

ただし、問題の説明にはより深い質問が隠されています。配列とその要素 (辞書) の両方を可変にしたいようですが、特にあなたの「他のオブジェクトに渡されるコピーは、オリジナルを変更せずに変更する必要がある」という規定。正確に何を意味するかに応じて、これを保証して実装するのは非常に複雑になる可能性があります。

たとえば、元の配列に多数の変更可能な辞書が含まれているとします。これらの最初の 2 つのレベルの変更可能なコピーを作成するということは、コピーを取得したユーザーが、元の配列や辞書を直接変更することなく、独自の配列と配列内の辞書を変更できることを意味します。ただし、ディクショナリに変更可能なオブジェクト (NSMutableArray、NSMutableString など) が含まれている場合、「コピー」を使用するコードは、変更可能なコピーへの参照のみを使用して、間接的にディクショナリの内容を変更できます。

変更可能なコピーは「浅い」ものです。つまり、最初のレベルのみがコピーされ、コピーには元の構造と同じ要素へのポインターがあります。したがって、オリジナルとコピーの間にリンクがないことを (少なくとも手動で) 保証するには、構造全体を一掃してコピーを作成する必要があります。一部の要素が NSCopying または NSMutableCopying に準拠していない場合、これはさらに複雑になる可能性があります。

最も簡単で最速の解決策は、上記の単純なコードを使用するか、単にマスター配列への参照を返すことです。これには、クライアント コードが配列を変更しないことを信頼する必要があるため、これはすべての状況で機能するとは限りませんが、呼び出しコードも制御する場合は、これが望ましい場合があります。一方、絶対に完全に別のコピーが必要な場合は、NSCoding / キー付きアーカイブの利用を検討してください。

NSMutableArray *masterArray = ...
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:masterArray];
NSMutableArray *clone = [NSKeyedUnarchiver unarchiveObjectWithData:data];

基本的に、これはすべてを生データバイトに変換し、それを新しいオブジェクトのセットに再構成します。これが機能するには、辞書内のすべてのオブジェクトがNSCoding プロトコルに準拠している必要があります。これには少し時間がかかる場合がありますが、オブジェクトの一意性を保証するための洗練された一般的な方法です。これには確かにパフォーマンス コストがゼロではありませんが、副作用がないことを絶対に保証する必要がある場合は、機能するはずです。

于 2009-07-28T16:06:41.440 に答える