web-dev-qa-db-ja.com

Objective-Cでオブジェクトをコピーする方法

独自のオブジェクトを持つカスタムオブジェクトをディープコピーする必要があります。 NSCopyingを継承する方法とNSCopyObjectを使用する方法について、私は読んでいて少し混乱しています。

109
ben

参照型と同様に、「コピー」には2つの概念があります。あなたはそれらを知っていると確信していますが、完全を期しています。

  1. ビット単位のコピー。ここでは、ビット単位でメモリをコピーするだけです-これがNSCopyObjectが行うことです。ほぼ常に、それはあなたが望むものではありません。オブジェクトには内部状態や他のオブジェクトなどがあり、多くの場合、オブジェクトはそのデータへの参照を保持している唯一のものであると仮定します。ビット単位のコピーはこの仮定を破ります。
  2. 深い論理コピー。これでは、オブジェクトのコピーを作成しますが、実際には少しずつ実行することなく-すべての意図と目的で同じように動作しますが、(必ずしも)オリジナルのメモリと同一のクローンではないオブジェクトが必要です- Objective Cのマニュアルでは、このようなオブジェクトを元のオブジェクトから「機能的に独立」と呼びます。これらの「インテリジェントな」コピーを作成するメカニズムはクラスごとに異なるため、オブジェクト自体に実行を依頼します。これはNSCopyingプロトコルです。

後者が必要です。これが独自のオブジェクトの1つである場合、NSCopyingプロトコルを採用し、-(id)copyWithZone:(NSZone *)zoneを実装するだけです。好きなことを自由に行うことができます。しかし、あなたは自分の本当のコピーを作り、それを返すという考えです。すべてのフィールドでcopyWithZoneを呼び出して、ディープコピーを作成します。簡単な例は

@interface YourClass : NSObject <NSCopying> 
{
   SomeOtherObject *obj;
}

// In the implementation
-(id)copyWithZone:(NSZone *)zone
{
  // We'll ignore the zone for now
  YourClass *another = [[YourClass alloc] init];
  another.obj = [obj copyWithZone: zone];

  return another;
}
188
Adam Wright

Appleのドキュメントによると

サブクラスバージョンのcopyWithZone:メソッドは、サブクラスがNSObjectから直接派生しない限り、メッセージを最初にスーパーに送信して実装を組み込む必要があります。

既存の回答に追加する

@interface YourClass : NSObject <NSCopying> 
{
   SomeOtherObject *obj;
}

// In the implementation
-(id)copyWithZone:(NSZone *)zone
{
  YourClass *another = [super copyWithZone:zone];
  another.obj = [obj copyWithZone: zone];

  return another;
}
23
Saqib Saud

私はそのコードと私のコードの違いを知りませんが、その解決策に問題があるので、もう少し読んで、返される前にオブジェクトを設定する必要があることがわかりました。私は次のようなものを意味します:

#import <Foundation/Foundation.h>

@interface YourObject : NSObject <NSCopying>

@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *line;
@property (strong, nonatomic) NSMutableString *tags;
@property (strong, nonatomic) NSString *htmlSource;
@property (strong, nonatomic) NSMutableString *obj;

-(id) copyWithZone: (NSZone *) zone;

@end


@implementation YourObject


-(id) copyWithZone: (NSZone *) zone
{
    YourObject *copy = [[YourObject allocWithZone: zone] init];

    [copy setNombre: self.name];
    [copy setLinea: self.line];
    [copy setTags: self.tags];
    [copy setHtmlSource: self.htmlSource];

    return copy;
}

私はこの問題に多くの問題があり、なぜそれが起こっているのか見当もつかないので、この答えを追加しました。私は違いを知りませんが、それは私のために働いており、おそらく他の人にとっても役立つかもしれません:)

21
Felipe Quirós
another.obj = [obj copyWithZone: zone];

objとして宣言されている(と仮定する)プロパティを介してretainにアクセスするため、この行はメモリリークを引き起こすと思います。したがって、retain countは、プロパティおよびcopyWithZoneによって増加します。

私はそれがあるべきだと信じています:

another.obj = [[obj copyWithZone: zone] autorelease];

または:

SomeOtherObject *temp = [obj copyWithZone: zone];
another.obj = temp;
[temp release]; 
2
Szuwar_Jr

->演算子を使用してコピーすることもできます。例えば:

-(id)copyWithZone:(NSZone*)zone
{
    MYClass* copy = [MYClass new];
    copy->_property1 = self->_property1;
    ...
    copy->_propertyN = self->_propertyN;
    return copy;
}

ここでの理由は、コピーされたオブジェクトが元のオブジェクトの状態を反映する必要があるためです。 「。」演算子は、ロジックを含む可能性があるゲッターを呼び出すため、副作用を引き起こす可能性があります。

0
Alex Nolasco