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


112

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


1
copy、mutableCopy、copyWithZoneを理解するための素晴らしいチュートリアル
horkavlna

回答:


192

参照型の場合と同様に、「コピー」には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;
}

しかし、あなたはコピーされたオブジェクトの受信者にそれを解放する責任を負わせています!あなたはautoreleaseそれをすべきではありませんか、それとも私はここで何か不足していますか?
bobobobo 2009

30
@bobobobo:いいえ、Objective-Cのメモリ管理の基本的なルールは次のとおりです。名前が「alloc」または「new」で始まるか「copy」を含むメソッドを使用してオブジェクトを作成すると、オブジェクトの所有権を取得します。copyWithZone:この基準を満たすため、保持カウントが+1のオブジェクトを返す必要があります。
Steve Madsen、

1
@Adam ゾーンが渡された後で、alloc代わりに使用する理由はありallocWithZone:ますか?
リチャード

3
まあ、ゾーンは最近のOS Xベースのランタイムでは事実上使用されていません(つまり、文字通り使用されていないと思います)。しかし、はい、あなたは呼び出すことができますallocWithZone
アダムライト


25

アップルのドキュメントは言う

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

2
YourClassはNSObjectから直接派生しているので、ここでは必要ないと思います
Mike

2
良い点ですが、クラス階層が長い場合の一般的なルールです。
Saqib Saud、2012

8
エラーが発生しました:No visible @interface for 'NSObject' declares the selector 'copyWithZone:'。これは、実装する他のカスタムクラスから継承する場合にのみ必要だと思いますcopyWithZone
Sam

1
another.obj = [[obj copyWithZone:zone] autorelease]; NSObjectのすべてのサブクラス。そして、プリミティブデータ型の場合は、それらを割り当てるだけです-> another.someBOOL = self.someBOOL;
hariszaman 2014

@Sam "NSObject自体はNSCopyingプロトコルをサポートしていません。サブクラスはプロトコルをサポートし、copyWithZone:メソッドを実装する必要があります。copyWithZone:メソッドのサブクラスバージョンは、最初にメッセージをスーパーに送信して、実装を組み込む必要があります(サブクラスが直接下降しない場合) NSObjectから。」 developer.apple.com/documentation/objectivec/nsobject/...
s4mt6

21

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

#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;
}

私はこの問題に多くの問題を抱えており、なぜそれが起こっているのかについての手掛かりがないので、この回答を追加しました。違いはわかりませんが、私には効果があり、他の人にも役立つかもしれません:)


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

この行はメモリリークを引き起こすと思います。なぜなら、obj(私は)として宣言されているプロパティを介してアクセスするからですretain。したがって、保持カウントはプロパティおよびによって増加しますcopyWithZone

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

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

または:

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

いいえ、メソッドalloc、copy、mutableCopy、newは自動解放されていないオブジェクトを返す必要があります。
kovpas 2012

@kovpas、よろしいですか、あなたは私を正しく理解していますか?返されたオブジェクトのことではなく、データフィールドのことです。
Szuwar_Jr 2012

ええ、私の悪い、ごめんなさい。マイナスを削除できるように、回答をどうにか編集していただけませんか。:))
kovpas 2012

0

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.