Copy(一):浅いコピー

4881 ワード

もし私が書いたのが悪くないと思ったら、私の新浪微博@楊浩宇-橘爺に注目してください.最新の文章はすぐにプッシュします.
前言
Copyについて説明する前に、付与とコピーの違いを振り返ってみましょう.
単純な代入文を使用してオブジェクトを別のオブジェクトに代入する場合は、次のようになります.
origin = pt;

この例では、originとptは両方とも2つの整数インスタンス変数オブジェクトである.このように付与された結果は、オブジェクトptのアドレスをoriginにコピーするだけである.割り当て操作が終了すると、両方の変数はメモリ内の同じアドレスを指します.メッセージを使用して、次のようなインスタンス変数を変更します.
origin = 100;

メモリ内の同じオブジェクトを参照するため、origin変数とpt変数が変更されました.
Foundationオブジェクトにも同様に適用されます.1つの変数を別のオブジェクトに割り当て、このオブジェクトへの別の参照のみを作成します.したがって、dataArrayとdataArray 2の両方がNSMutableArrayオブジェクトである場合、文:
dataArray2 = dataArray;
[dataArray2 removeObjectAtIndex:0];

この2つの変数が参照する同じ配列から最初の要素が削除されます.
copyとmutableCopyメソッド
Foundationクラスでは、copyとmutableCopyというメソッドが実装されており、これらのメソッドを使用してオブジェクトのコピーを作成できます.このタスクは、レプリカを作成するためのプロトコルに合致する方法を実装することによって達成されます.クラスが生成するオブジェクトが可変コピーか非可変コピーかを区別する必要がある場合は、プロトコルに基づいてメソッドを実装する必要があります.
Foundationクラスのcopyメソッドをレビューし、前述の2つのNSMutableArrayオブジェクトdataArray 2とdataArray、文を与えます.
dataArray2 = [dataArray mutableCopy];

メモリに新しいdataArrayコピーを作成し、そのすべての要素をコピーしました.次に、文を実行します.
[dataArray2 removeObjectAtIndex: 0];

dataArray 2の最初の要素は削除されますが、dataArrayの最初の要素は削除されません.
ただし、オブジェクトの可変コピーを生成するには、コピーされたオブジェクト自体が可変である必要はありません.この場合は、可変コピーにも適用されます.可変オブジェクトの可変コピーを作成できます.
実装プロトコル
独自のクラス(アドレス帳など)のcopyメソッドを使用しようとすると、文は次のようになります.
AddressBook *newBook = [myBook mutableCopy];

エラーメッセージが表示されます.
*** -[AddressBook copyWithZone:]: selector not recognized

前述したように、独自のクラスを使用してレプリケーションを実装するには、プロトコルに基づいて1つまたは2つの方法を実装する必要があります.
Fractionクラスにcopyメソッドを追加する方法を示します.ここで説明するレプリケーションポリシーのテクニックは、自分のクラスに非常に適しています.これらのクラスが任意のFoundationクラスのサブクラスである場合、より複雑なレプリケーションポリシーを実装する必要がある場合があります.スーパークラスが独自のレプリケーションポリシーを実装している可能性があるという事実を考慮する必要があります.
プロトコルを実装する場合、クラスはcopyWithZone:copyメッセージに応答する方法を実装する必要があります(このcopyメッセージはnilパラメータを持つcopyWithZone:メッセージをあなたのクラスに送信するだけです).なお、可変コピーと非可変コピーを区別するには、プロトコルに従ってmutableCopyWithZone:を実装すると、可変コピーが返されます.オブジェクトの可変コピーを生成するには、コピーされたオブジェクト自体も可変である必要はありません(逆も同様です).可変オブジェクトの可変コピーを生成するには合理的です(たとえば、文字列オブジェクトを考慮します).
@interfaceコマンドは次のとおりです.
@interface Fraction : NSObject 

FractionはNSObjectのサブクラスであり、NSCopyプロトコルに準拠しています.実装ファイルFraction.mで、次の定義を新しいメソッドに追加します.
- (id) copyWithZone: (NSZone *) zone
{
    Fraction *newFract = [Fraction allocWithZone: zone] init];
    [newFract setTo: numerator over: denominator];  
    return newFract;
}

パラメータzoneは異なるストレージ領域に関係しており、プログラムにこれらのストレージ領域を割り当てて使用することができます.これらのzoneパラメータは、大量のメモリを割り当てるアプリケーションを作成し、これらのストレージ領域に空間割り当てをグループ化してメモリ割り当てを最適化する場合にのみ処理する必要があります.copyWithZone:に渡された値を使用し、allocWithZone:というメモリ割り当て方法に渡すことができます.このメソッドは、指定したストレージ領域にメモリを割り当てます.
新しいFractionオブジェクトを割り当てた後、受信者のnumerator変数とdenominator変数をコピーします.copyWithZone:メソッドはオブジェクトの新しいコピーを返すはずです.このオブジェクトはあなたのメソッドで実現されます.
クラスがサブクラスを生成できる場合は、copyWithZone:メソッドが継承されます.この場合、このメソッドのプログラム行
Fraction *newFract = [[Faction allocWithZone: zone] init];

に改めるべきだ
id newFract = [[[self class] allocWithZone: zone] init];

これにより、クラスから新しいオブジェクトを割り当てることができます.このクラスはcopyの受信者です(たとえば、NewFractionというサブクラスが生成された場合、Fractionオブジェクトではなく継承メソッドに新しいNewFractionオブジェクトが割り当てられていることを確認する必要があります).
クラスのcopyWithZone:メソッドを記述し、クラスのスーパークラスもプロトコルを実装している場合は、継承されたインスタンス変数をコピーするためにスーパークラスのcopyメソッドを呼び出し、その後、自分のコードを追加してクラスに追加したい追加のインスタンス変数(もしあれば)をコピーする必要があります.
@propertyのcopy
前述の内容を総合すると、操作がうっかりして他のオブジェクトの値を変更しないように、setterメソッドでオブジェクトをコピーするのが安全です.
合成setterメソッドがない場合は、次のようにcopyバージョンを使用したsetterメソッドを記述できます.-(void)setName:(NSString*)theName{name=[theName copy];}属性宣言でcopyプロパティが指定されている場合、合成メソッドはクラスのcopyメソッド(親から作成または継承)を使用し、propertyは次のように宣言します.
@property (nonatomic, copy) NSString *name;

@synthesize命令とともに使用され、生成された方法は次の方法と動作します.
- (void) setName:(NSString *)theName
{
    if(theName != name) {
        name = [theName copy];
    }
}

注意:プロパティにはmutableCopyプロパティはありません.可変インスタンス変数でも、メソッドcopyWithZoneの実行結果のようにcopyプロパティが使用されます.規則に従ってオブジェクトの可変コピーが生成されます.
インスタンス変数値の保護に関する議論はgetter関数にも適用されます.可変オブジェクトを返す場合は、戻り値の変更がインスタンス変数の値に影響しないことを確認する必要があります.この場合、インスタンス変数のコピーを生成し、元の値に代わって戻り値として使用できます.
copyメソッドの実装に戻り、コピー中のインスタンス変数に可変文字列オブジェクトなどの可変オブジェクトが含まれている場合、このオブジェクトコンテンツの新しいコピーを生成する必要はありません.オブジェクトを保持して新しいリファレンスを生成するだけで十分かもしれません.たとえば、AddressCardのcopyメソッドを実装する場合、このクラスにはnameとemailメンバーが含まれています.次に実装されるcopyWithZone:メソッドで十分です.-(AddressCard *) copyWithZone:(NSZone *)zone { AddressCard *newCard = [[AddressCard allocWithZone:zone] init];
    [newCard assignName: name andEmail: email];
    return newCard;
}

- (void)assignName:(NSString *) theName andEmail: (NSString *) theEmail
{
    name = theName;
    email = theEmail;
}