NSStringプロパティ:コピーまたは保持しますか?


331

プロパティ名で呼び出されたクラスがあるとSomeClassしましょうstring

@interface SomeClass : NSObject
{
    NSString* name;
}

@property (nonatomic, retain) NSString* name;

@end

名前にが割り当てられている可能性があることを理解していNSMutableStringます。その場合、誤動作につながる可能性があります。

  • 一般的に文字列の場合、代わりに属性を使用することは常に良い考えですか?copyretain
  • 「コピーされた」プロパティは、そのような「保持された」プロパティよりも効率が悪いのですか?

6
追加質問:nameで解放するdealloc必要がありますか?
Chetan

7
@chetanはい、そうすべきです!
Jon

回答:


440

タイプがNSCopyingプロトコルに準拠する不変の値クラスである属性の場合、ほとんどの場合copy@property宣言で指定する必要があります。retainこのような状況では、指定することはほとんどありません。

その理由は次のとおりです。

NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];

Person *p = [[[Person alloc] init] autorelease];
p.name = someName;

[someName setString:@"Debajit"];

現在の値Person.nameプロパティは、プロパティが宣言されているかどうかによって異なりますretaincopy、それはなります- @"Debajit"プロパティがマークされている場合retain、しかし、@"Chris"プロパティがマークされている場合copy

ほとんどすべての場合、オブジェクトの属性が背後で変化するのを防ぎたいので、それらを表すプロパティをマークする必要がありますcopy。(そして、あなたが使用するのではなく、セッターを自分で書く場合@synthesize、あなたが実際に使用するために覚えておく必要があるcopyの代わりに、retainそれに。)


61
この回答により、混乱が生じた可能性があります(robnapier.net/blog/implementing-nscopying-439#comment-1312を参照)。あなたはNSStringについて完全に正しいですが、私はあなたがポイントを少し一般的すぎると言ったと思います。NSStringをコピーする必要があるのは、共通の可変サブクラス(NSMutableString)があるためです。変更可能なサブクラスを持たないクラス(特に、自分で作成したクラス)の場合、時間とメモリの浪費を避けるために、通常はコピーするよりも保持しておくことをお勧めします。
Rob Napier

63
あなたの推論は正しくありません。時間/メモリに基づいてコピーするか保持するかを決定するのではなく、目的のセマンティクスに基づいて決定する必要があります。そのため、私は「不変値クラス」という用語を具体的に使用しました。また、クラスに可変サブクラスがあるか、それ自体が可変であるかは問題ではありません。
Chris Hanson

10
Obj-Cがタイプごとに不変性を強制できないのは残念です。これは、C ++の推移的なconstの欠如と同じです。個人的には、文字列は常に不変であるかのように動作ます。可変文字列を使用する必要がある場合、後で変更する可能性があっても不変参照を渡すことはありません。違うものはコードのにおいだと思います。その結果、私のコード(私が単独で作業している)では、すべての文字列に対して保持を使用しています。私がチームの一員として働いていた場合、私は物事を別様に見るかもしれません。
philsquared 2010年

5
@Phil Nash:一人で作業するプロジェクトと他の人と共有するプロジェクトに異なるスタイルを使用するのはコードのにおいだと思います。すべての言語/フレームワークには、開発者が同意する共通のルールまたはスタイルがあります。プライベートプロジェクトでそれらを無視するのは間違っているようです。そして、「私のコードでは、可変文字列を返さない」という根拠については、それはあなた自身の文字列では機能するかもしれませんが、フレームワークから受け取る文字列については決して知りません。
Nikolai Ruhe

7
@Nikolai NSMutableString一時的な「文字列ビルダー」タイプ(私はすぐに不変のコピーを取得する)を除いて、を使用しません。私はそれらを控えめなタイプにすることを望みますが、元の文字列が変更可能でない場合、コピーが自由に保持できるという事実は、私の懸念のほとんどを軽減します。
philsquared、

120

NSStringにはコピーを使用する必要があります。Mutableの場合、コピーされます。そうでない場合は、保持されます。アプリに必要なセマンティクスそのもの(型に最善を尽くさせる)。


1
私は依然として可変で不変の形式を控えめにすることを望みますが、元の文字列が不変である場合、そのコピーが保持される可能性があることに気づいていませんでした。ありがとう。
philsquared 2010年

25
その言及のための1 NSStringプロパティは、として宣言さcopyれますretain(それはもちろん、不変の場合)とにかく。私が考えることができる他の例はNSNumberです。
matm

この回答と@GBYによる反対票の違いは何ですか?
ゲイリーリン2013年

67

一般的に文字列の場合、保持ではなくコピー属性を使用することは常に良い考えですか?

はい-通常、常にコピー属性を使用します。

これは、NSStringプロパティにNSStringインスタンスまたはNSMutableStringインスタンスを渡すことができるため、渡される値が不変オブジェクトか可変オブジェクトかを実際に判断できないためです。

「コピーされた」プロパティは、そのような「保持された」プロパティよりも効率が悪いのですか?

  • プロパティにNSStringインスタンスが渡されている場合、答えは「いいえ」です-コピーは保持よりも効率的ではありません
    (NSStringは実際にはコピーを実行しないほどスマートなので、それほど効率的ではありません。)

  • プロパティにNSMutableStringインスタンスが渡された場合、答えは「はい」です-コピーは保持よりも効率的ではありません。
    (実際のメモリの割り当てとコピーが発生するため、効率は低下しますが、これはおそらく望ましいことです。)

  • 一般的に言えば、「コピーされた」プロパティは効率が低下する可能性があります。ただし、NSCopyingプロトコルを使用することで、保持するのと同じくらい効率的にコピーするクラスを実装できます。NSStringインスタンスはこの例です。

一般的に(NSStringだけでなく)、「保持」ではなく「コピー」をいつ使用すればよいですか?

copy警告なしにプロパティの内部状態を変更したくない場合は、常に使用する必要があります。不変オブジェクトの場合でも-適切に記述された不変オブジェクトは、コピーを効率的に処理します(不変に関する次のセクションを参照NSCopying)。

retainオブジェクトにはパフォーマンス上の理由があるかもしれませんが、メンテナンスのオーバーヘッドが伴います。内部状態がコードの外部で変化する可能性を管理する必要があります。彼らが言うように-最後に最適化します。

しかし、私は自分のクラスを不変であるように作成しました-それを単に「保持」することはできませんか?

使用しないcopy。クラスが実際に不変である場合は、NSCopyingプロトコルが実装されているときにクラスがそれ自体を返すようにプロトコルを実装することがベストプラクティスcopyです。これを行う場合:

  • クラスの他のユーザーがを使用すると、パフォーマンスのメリットが得られますcopy
  • copyアノテーションは、独自のコードをより保守しやすくします。アノテーションは、copyこのオブジェクトが他の場所で状態を変更することについて心配する必要がないことを示しています。

39

私はこの単純なルールに従うようにしています:

  • プロパティに割り当てる時点でオブジェクトのを保持しますか?コピーを使用します

  • 私はを保持したいですオブジェクト私はその内部の値が何を気にしない現在または将来のでしょうか?強い(保持)を使用します。

説明するためにします。Do Iはを保持したい名前「リサ・ミラー」(コピー)または私はを保持したいの人物リサ・ミラー(強いですか)?彼女の名前は後で「リサ・スミス」に変更される可能性がありますが、彼女は同じ人物のままです。


14

この例では、コピーと保持は次のように説明できます。

NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];

Person *p = [[[Person alloc] init] autorelease];
p.name = someName;

[someName setString:@"Debajit"];

プロパティのタイプがコピーの場合、

[Person name]文字列の内容を保持する文字列の新しいコピーが作成されsomeNameます。これで、someNamestring に対する操作はに影響を与えません[Person name]

[Person name]そして、someName文字列が異なるメモリアドレスを持っています。

しかし、保持の場合、

どちらも[Person name]somename文字列と同じメモリアドレスを保持しますが、somename文字列の保持カウントが1だけ増加します。

そのため、somename文字列の変更は文字列に反映され[Person name]ます。


3

確かに、プロパティ宣言に「コピー」を置くと、ヒープ上のオブジェクトが参照によって渡されるオブジェクト指向の環境を使用する場合に直面します-ここで得られる利点の1つは、オブジェクトを変更するときに、そのオブジェクトへのすべての参照です。最新の変更を確認してください。多くの言語が「ref」または類似のキーワードを提供して、値のタイプ(つまり、スタック上の構造)が同じ振る舞いから利益を得られるようにします。個人的には、私は控えめにコピーを使用し、割り当てられたオブジェクトに加えられた変更からプロパティ値を保護する必要があると感じた場合、割り当て中にそのオブジェクトのコピーメソッドを呼び出すことができます。

p.name = [someName copy];

もちろん、そのプロパティを含むオブジェクトをデザインするとき、割り当てがコピーを取得するパターンからデザインが恩恵を受けるかどうかはあなただけが知っています-Cocoawithlove.comは次のように言っています:

「セッターパラメーターが変更可能であるが、プロパティの内部状態を警告なしに変更できない場合は、コピーアクセサーを使用する必要があります。」-予期せず変更する値に耐えられるかどうかの判断は、すべてあなた次第です。このシナリオを想像してください:

//person object has details of an individual you're assigning to a contact list.

Contact *contact = [[[Contact alloc] init] autorelease];
contact.name = person.name;

//person changes name
[[person name] setString:@"new name"];
//now both person.name and contact.name are in sync.

この場合、コピーを使用せずに、連絡先オブジェクトは自動的に新しい値を取得します。ただし、使用した場合は、変更が検出されて同期されたことを手動で確認する必要があります。この場合、意味を保持することが望ましい場合があります。別の方法では、コピーの方が適切な場合があります。


1
@interface TTItem : NSObject    
@property (nonatomic, copy) NSString *name;
@end

{
    TTItem *item = [[TTItem alloc] init];    
    NSString *test1 = [NSString stringWithFormat:@"%d / %@", 1, @"Go go go"];  
    item.name = test1;  
    NSLog(@"-item.name: point = %p, content = %@; test1 = %p", item.name, item.name, test1);  
    test1 = [NSString stringWithFormat:@"%d / %@", 2, @"Back back back"];  
    NSLog(@"+item.name: point = %p, content = %@, test1 = %p", item.name, item.name, test1);
}

Log:  
    -item.name: point = 0x9a805a0, content = 1 / Go go go; test1 = 0x9a805a0  
    +item.name: point = 0x9a805a0, content = 1 / Go go go, test1 = 0x9a84660

0

NSStringプロパティを宣言するには、常にcopyを使用する必要があります

@property (nonatomic, copy) NSString* name;

不変文字列を返すか(可変文字列が渡された場合)、保持された文字列を返すか(不変文字列が渡された場合)の詳細については、これらを読む必要があります。

NSCopyingプロトコルリファレンス

クラスとそのコンテンツが不変である場合、新しいコピーを作成する代わりに、元のコピーを保持することでNSCopyingを実装します

値オブジェクト

したがって、不変バージョンでは、次のようにすることができます。

- (id)copyWithZone:(NSZone *)zone
{
    return self;
}

-1

nameは(不変)NSStringであるNSStringため、nameに別の名前を設定しても、コピーまたは保持による違いはありません。つまり、コピーは保持と同じように動作し、参照カウントを1つ増やします。不変であり、クローンを作成する必要がないため、これは不変のクラスの自動最適化だと思います。ただし、a NSMutalbeString mstrをnameに設定するとmstr、正確さのためにの内容がコピーされます。


1
宣言した型と実際の型を混同しています。「保持」プロパティを使用してNSMutableStringを割り当てると、そのNSMutableStringは保持されますが、変更は可能です。「copy」を使用すると、NSMutableStringを割り当てるときに不変のコピーが作成されます。それ以降、変更可能な文字列のコピー自体は不変であるため、プロパティの「コピー」は保持されます。
gnasher729 2014年

1
ここにいくつかの重要な事実がありません。保持された変数からのオブジェクトを使用する場合、その変数が変更されると、オブジェクトも変更されます。コピーされた変数からのものである場合、オブジェクトには変数の現在の値が含まれます。変更されません
ブライアンP

-1

文字列が非常に大きい場合、コピーはパフォーマンスに影響し、大きな文字列の2つのコピーはより多くのメモリを使用します。

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