3

さまざまなオブジェクトを操作または作成するときに、何が起こるのか、その理由を理解しようとしています。(うまくいけば、ドキュメントから学ぶことができます。)

「Objective-C 2.0 でのプログラミング」(第 2 版、Steven Kochan 著) を読んでいます。408 ページの最初の段落には、保持カウントの説明があります。

その参照カウントは 2 になることに注意してください。addObject:メソッドはこれを自動的に行います。メソッドのドキュメントを確認するaddObject:と、この事実がそこに記載されていることがわかります。

だから私はaddObject:ドキュメントを読みました:

配列の末尾に特定のオブジェクトを挿入します。

そこには、説明がありませんが、 のような他の項目には次のようarrayByAddingObject:に記載されています。

指定されたオブジェクトが最後に追加された受信配列のコピーである新しい配列を返します。

参照のどこでaddObject:保持カウントが増加することを示していますか? ARC の存在を考えると、これらのメソッドがバグや問題を回避するために何をしているのかを理解する必要があります。ARC はこれに何をもたらしますか? (読み直そうかな…)

4

3 に答える 3

7
于 2012-05-26T20:13:40.997 に答える
6

あなたが正しいので、本は古いドキュメントを参照しているに違いありません。保持カウントについては何も言及していません。ただし、実際にはオブジェクトを保持します。あなたがそれを考える必要がある方法は、(役に立たない)保持数の観点からではなく、むしろ所有権の観点からです。特にARCを使用する場合はそうです。

オブジェクトを に追加すると、オブジェクトNSMutableArrayはそのオブジェクトの所有権を取得します (ARC 用語では、オブジェクトへの強い参照があります)。

「ARCはこれに何をもたらしますか?」

ARC は何も変わりません。ARC が (いくつかの最適化を除いて) 行うことは、ARC を使用せずに自分で追加する場合と同じ release、retain、および autorelease ステートメントを追加することだけです。気にする必要があるのは、オブジェクトを配列に追加すると、少なくとも配列と同じくらい存続することだけです。


メソッドは、渡すオブジェクトを含むarrayByAddingObject:新しいNSArray(またはNSMutableArray) を作成し、渡されたオブジェクトへの強い参照を保持します。作成される実際の配列オブジェクトには、ivar、プロパティ、またはローカル変数のいずれかに割り当てない限り、まだ参照がありません。何に割り当てるかによって、寿命が決まります。


基本的に ARC がなくても、所有権の観点からオブジェクトのライフサイクルを考えるのが最善です。ARC はそれを形式化するだけです。そのため、フレームワークを使用する場合、保持がいつ発生するか発生しないかは関係ありません。所有権を別のオブジェクトに渡すまで、オブジェクトに対してのみ責任があり、フレームワークがオブジェクトを存続させると信頼できます。それが必要な限り。

もちろん、所有権を構成するものを直観する必要があります。たとえば、デリゲート プロパティは、循環保持サイクル (2 つのオブジェクトが互いに保持される) を防ぐために、assign、または ARCunsafe_unretainedまたはであることがよくありますが、保持/強力な場合があるため、ケースバイケースでそれらを調べる必要があります。weak

また、キー値の監視や NSNotification のような場合、監視しているオブジェクトを監視してもオブザーバーは保持されません。

しかし、それらはルールの例外です。通常、強力な参照を想定できます。

上記のこの文について: 「作成する実際の配列オブジェクトには、ivar、プロパティ、またはローカル変数のいずれかに割り当てない限り、まだ参照がありません。割り当て先によって寿命が決まります。」私は説明しようとします:

このコードを実行すると、 (オブジェクト タイプに応じて)[someArray arrayByAddingObject:someObject];新しいNSArrayまたはオブジェクトがインスタンス化されますが、実際には参照に割り当てられていません。つまり、ARC を使用している場合は、その後すぐに解放される可能性があります。ARC を使用していない場合は、autoreleasepool が空になったときに解放されます (おそらく、そのスレッドの実行ループの次の反復で)。NSMutableArraysomeArray

代わりにこれを行った場合: NSArray *someOtherArray = [someArray arrayByAddingObject:someObject];someOtherArray と呼ばれる、新しく作成された配列への参照ができました。この場合、これはローカル変数であり、スコープは{ }それが存在するセット内にのみあります(したがって、ifステートメント、ループ、またはメソッド内にある可能性があります。これで他に何もしないと、後で死ぬでしょうスコープが終了します (すぐに死ぬことが保証されているわけではありませんが、それは重要ではありません。それが長く続くと想定することはできません)。

クラスのヘッダーで iVar (インスタンス変数) が宣言されている場合NSArray *someOtherArray;(ARC ではデフォルトで強力) someOtherArray = [someArray arrayByAddingObject:someObject];、クラスのどこかで実行すると、オブジェクトは参照 ( someOtherArray = nil) を削除するか、上書きするまで存続します。参照 ( someOtherArray = someThirdArray)、またはクラスの割り当てが解除されます。ARC を使用していない場合は、同じ効果を得るためにそれを保持する必要があります (someOtherArray = [[someArray arrayByAddingObject:someObject] retain];これは基本的に ARC が舞台裏で行っていることです)。

@property (nonatomic, strong) NSArray *someOtherArrayまたは、同じ効果を達成する代わりにinのようにプロパティを宣言することもできますがself.someOtherArray = [someArray arrayByAddingObject:someObject];、proprety アクセサー ( setSomeOtherArray:) を使用するかsomeOtherArray = [someArray arrayByAddingObject:someObject];、iVar を直接設定するために使用することもできます (@synthesizedそれを前提としています)。

または、非 ARC を想定して、 ARC とまったく同じように動作する@property (nonatomic, retain) NSArray *someOtherArrayinのようなプロパティを宣言した可能性がありますself.someOtherArray = [someArray arrayByAddingObject:someObject];が、iVar を直接設定する場合は、retain を手動で追加する必要があります。

少しでも問題が解決することを願っています。見落としや省略があれば教えてください。


コメントで述べたように、ここでの鍵は、オブジェクトが別のオブジェクトによって所有されていると見なされるかどうかを直感的に知ることです。幸いなことに、Cocoa フレームワークは、安全な仮定を行うことを可能にする一連のかなり厳密な規則に従っています。

  • NSStringフレームワーク オブジェクトのプロパティ (たとえばtexta のプロパティなど) を設定すると、UILabel常にコピーされます (誰かが反例を知っている場合は、コメントまたは編集してください)。そのため、一度渡した文字列について心配する必要はありません。文字列がコピーされるのは、変更可能な文字列が渡された後に変更されないようにするためです。
  • 以外の他のプロパティを設定するdelegateと、(ほぼ?) 常に保持されます (または ARC での強い参照)。
  • デリゲート プロパティを設定するときは、(ほとんど?) 常に代入 (または弱参照) して循環保持サイクルを防ぎます。(たとえば、オブジェクトaには、強参照されるプロパティbb、強参照されるデリゲート プロパティがあるとします。aのデリゲートとして設定しbます。現在a、 とbは両方とも互いに強く参照しており、どちらのオブジェクトも保持カウントが 0 に達することはありません。デリゲートNSURLConnectionはメソッドを介して設定されるため、デリゲートを強く参照する反例です -- 以下の規則を参照してください --NSURLConnection完了後にnil out または解放する規則ですではなくdealloc、循環保持を削除します)
  • 配列またはディクショナリに追加すると、常に保持されます (または強い参照)。
  • メソッドを呼び出してブロックを渡すと、それらは常にコピーされて、スタック (パフォーマンスのために最初に作成された場所) からヒープに移動されます。
  • オブジェクトのパラメーターを取り、すぐに結果を返さないメソッドは (常に? そうでないものは考えられません)、メソッドが確実に実行できるように渡すパラメーターをコピーまたは保持 (強い参照) します。彼らと一緒に必要なもの。たとえばNSURLConnection、メソッドを介して渡されるためデリゲートも保持されますが、他のオブジェクトのデリゲートプロパティを設定すると、それが規則であるため保持されません。

一貫性を保つために、独自のクラスでもこれらの同じ規則に従うことをお勧めします。

また、すべてのクラスのヘッダーを使用できることを忘れないでください。そのため、プロパティが保持または割り当て (または強いか弱い) であるかを簡単に確認できます。メソッドがそのパラメーターをどのように処理するかを確認することはできませんが、パラメーターは受信者が所有するという慣習があるため、その必要はありません。

于 2012-05-26T18:59:29.970 に答える
5

一般に、Cocoa APIの情報については、「最もグローバルな」場所を探す必要があります。メモリ管理はシステムAPI全体に浸透しており、APIはCocoaメモリ管理ポリシーの実装に一貫性があるため、Cocoaメモリ管理ガイドを読んで理解する必要があります。

理解すれば、明示的に文書化されていない限り、すべてのシステムAPIがそのメモリ管理ポリシーに実装されていると安全に想定できます。

したがって、NSMutableArrayのaddObject:メソッドの場合、配列に追加さ retainたオブジェクトに追加する必要があります。そうしないと、その標準ポリシーに違反します。

これは、ドキュメント全体で確認できます。これにより、すべてのメソッドのドキュメントが1ページ以上長くなるのを防ぎまれなメソッドまたはクラスが、何らかの理由で(場合によってはあまり良くない)ルールの例外である何かを実装する場合に明らかになります。


メモリ管理ガイドの「基本的なメモリ管理ルール」セクション:

保持を使用してオブジェクトの所有権を取得できます。

受信したオブジェクトは通常、受信したメソッド内で有効であることが保証されており、そのメソッドはオブジェクトを呼び出し元に安全に返すこともできます。次の2つの状況でretainを使用します。(1)アクセサーメソッドまたはinitメソッドの実装で、プロパティ値として格納するオブジェクトの所有権を取得する。(2)他の操作の副作用としてオブジェクトが無効になるのを防ぐため(「使用しているオブジェクトの割り当て解除の原因を回避する」で説明されています)。

(2)が鍵です。NS {Mutable} Arrayはretain、何らかの副作用によって追加されたオブジェクトが無効になるのを防ぐ必要があるため、追加されたオブジェクトが必要です。そうしないと、上記の規則とは異なるため、明示的に文書化されます。

于 2012-05-26T22:08:49.077 に答える