CocoaとObjective-Cを使用した参照カウントについて


122

iPhone SDKで遊ぶために、Objective-CとCocoaを検討し始めたところです。私はC mallocfree概念にかなり満足していますが、Cocoaの参照カウントスキームは、かなり混乱しています。あなたがそれを理解すればそれは非常に優雅であると言われます、しかし私はまだこぶを超えていません。

どのようにreleaseretainそしてautorelease機能し、それらの使用に関する慣習は何ですか?

(または失敗した場合、それを手助けするために何を読みましたか?)

回答:


148

retainand から始めましょうreleaseautorelease基本的な概念を理解すれば、本当に特別なケースです。

Cocoaでは、各オブジェクトは参照されている回数を追跡します(具体的には、NSObject基本クラスがこれを実装します)。retainオブジェクトを呼び出すことにより、参照カウントを1つ増やしたいことを伝えます。を呼び出すことでrelease、手放すオブジェクトを指定し、その参照カウントをデクリメントします。を呼び出した後release、参照カウントがゼロになった場合、そのオブジェクトのメモリはシステムによって解放されます。

基本的な方法この異なるのmallocfree、任意のオブジェクトが、あなたは彼らが使用していたメモリを解放してきたので、クラッシュ、システムの他の部分を心配する必要はないということです。全員がルールに従って遊び、保持/解放していると仮定すると、1つのコードがオブジェクトを保持してから解放しても、オブジェクトを参照している他のコードは影響を受けません。

何時々混乱することができますことは、あなたが呼び出す必要があり、その下の状況を知ることであるretainとしますrelease。私の一般的な経験則では、オブジェクトにしばらく(たとえば、それがクラスのメンバー変数である場合)ハングアップしたい場合は、オブジェクトの参照カウントが私について知っていることを確認する必要があります。上記のように、オブジェクトの参照カウントは、を呼び出すことで増加しretainます。慣例により、オブジェクトが「init」メソッドを使用して作成されると、増分されます(実際には1に設定されます)。これらのいずれの場合でもrelease、オブジェクトを使い終わったら、それを呼び出すのは私の責任です。そうしないと、メモリリークが発生します。

オブジェクト作成の例:

NSString* s = [[NSString alloc] init];  // Ref count is 1
[s retain];                             // Ref count is 2 - silly
                                        //   to do this after init
[s release];                            // Ref count is back to 1
[s release];                            // Ref count is 0, object is freed

今のためにautorelease。自動解放は、しばらくするとこのオブジェクトを解放するようにシステムに指示するための便利な(場合によっては必要な)方法として使用されます。配管の観点から見ると、autoreleaseが呼び出されると、現在のスレッドNSAutoreleasePoolに呼び出しが通知されます。NSAutoreleasePool今では(イベントループの現在の反復後)機会を得ると、それが呼び出すことができることを知っているreleaseオブジェクトの上に。プログラマーとしての私たちの観点から、それはrelease私たちを呼び出すことを処理しますので、私たちはする必要はありません(そして実際、私たちはすべきではありません)。

注意すべき重要な点は、(ここでも慣例により)すべてのオブジェクト作成クラスメソッドが自動解放オブジェクトを返すことです。たとえば、次の例では、変数 "s"の参照カウントは1ですが、イベントループが完了すると破棄されます。

NSString* s = [NSString stringWithString:@"Hello World"];

その文字列を使いretain続けたい場合は、明示的に呼び出して、release完了したら明示的に呼び出す必要があります。

以下の(非常に工夫された)コードを検討してください。autorelease必要な状況が表示されます。

- (NSString*)createHelloWorldString
{
    NSString* s = [[NSString alloc] initWithString:@"Hello World"];

    // Now what?  We want to return s, but we've upped its reference count.
    // The caller shouldn't be responsible for releasing it, since we're the
    // ones that created it.  If we call release, however, the reference 
    // count will hit zero and bad memory will be returned to the caller.  
    // The answer is to call autorelease before returning the string.  By 
    // explicitly calling autorelease, we pass the responsibility for
    // releasing the string on to the thread's NSAutoreleasePool, which will
    // happen at some later time.  The consequence is that the returned string 
    // will still be valid for the caller of this function.
    return [s autorelease];
}

これらすべてが少し混乱していることに気づきました-ある時点で、それはクリックします。ここにあなたを始めるためのいくつかの参照があります:

  • Appleによるメモリ管理の紹介
  • Aaron HillegasによるMac OS X(第4版)用のCocoaプログラミング -多くの優れた例を含む非常によく書かれた本。チュートリアルのように読みます。
  • あなたが本当に飛び込んでいるなら、あなたはビッグ・ナード・ランチに向かうことができます。これは、上記の本の著者であるAaron Hillegasが運営するトレーニング施設です。私は数年前にココアの紹介コースに参加しましたが、それは学ぶのに素晴らしい方法でした。

8
「autoreleaseを呼び出すことで、参照カウントを一時的に増やします」と書きました。これは間違っていると思います。自動解放は、将来解放されるオブジェクトをマークするだけで、参照カウントは増加しません:cocoadev.com/index.pl?AutoRelease
LKM

2
「自動解放の準備ができました。自動解放は、しばらくするとこのオブジェクトを解放するようにシステムに指示するための便利な(場合によっては必要な)方法として使用されます。」導入文として、これは誤りです。システムに「解放する」ように指示するのではなく、保持カウントをデクリメントするよう指示します。
mmalc '20年

3
良い説明をありがとう。まだ不明な点が1つだけあります。NSString* s = [[NSString alloc] initWithString:@"Hello World"];(あなたが書いたように)自動解放されたオブジェクトを返す場合、なぜ私はaを実行してreturn [s autorelease];それを「自動解放」に設定する必要があるのreturn sですか?
znq

3
@Stefan:[[NSString alloc] initWithString:@"Hello World"]自動解放されたオブジェクトを返しません。alloc呼び出されるたびに、参照カウントは1に設定され、解放されることを確認するのはそのコードの責任です。[NSString stringWithString:]コールは、他の一方で、ない自動解放オブジェクトを返します。
Matt Dillard、

6
楽しいトリビア:答えは@ ""とNSStringを使用しているため、文字列は全体にわたって一定であり、したがって絶対保持数は一定であり、完全に無関係です。...決して、答えを間違ってしまうことはありません。絶対保持カウントは本当に心配すべきものではないという事実を強調します。
bbum 2010

10

保持/解放のプロセスを理解している場合は、確立されたCocoaプログラマには明らかである2つのゴールデンルールがありますが、残念ながら、初心者にはこれが明確に記述されていることはほとんどありません。

  1. オブジェクトを返す関数を持っている場合alloccreateまたはcopyその名前でそのオブジェクトは、あなた次第です。[object release]使い終わったら電話する必要があります。またはCFRelease(object)、それがCore-Foundationオブジェクトの場合。

  2. 名前にこれらの単語の1つが含まれていない場合、オブジェクトは他の誰かに属しています。[object retain]関数の終了後もオブジェクトを保持する場合は、呼び出す必要があります。

自分で作成した関数では、この規則にも従うことができます。

(Nitpickers:はい、残念ながら、これらのルールの例外であるいくつかのAPI呼び出しがありますが、まれです)。


11
これは不完全で不正確です。:私は人々が規則を繰り返すように試みるのではなく、単に関連文書を指し理由を理解するために失敗し続けdeveloper.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/...
mmalc

4
特にコアファンデーションのルールはココアのルールとは異なります。developer.apple.com/documentation/CoreFoundation/Conceptual/…を
mmalc 2008年

1
私も同意しません。関数が所有したくないものを返す場合は、それを自動解放する必要があります。それを保持するのは、(必要に応じて)関数ジョブの呼び出し元です。呼び出されるメソッドの名前とは何の関係もないはずです。これは、オブジェクトの所有権が不明なCスタイルのコーディングです。
Sam

1
ごめんなさい!私は急いで反対票を投じたと思います。 メモリ管理のルールあなたの答えはほぼアップルのドキュメントを引用しています。
Sam

8

デスクトップ用のコードを作成していて、Mac OS X 10.5をターゲットにできる場合は、少なくともObjective-Cガベージコレクションの使用を検討する必要があります。開発の大部分が本当に簡素化されます—それが、Appleがそもそもの開発に全力を注いで、うまく機能させる理由です。

GCを使用しない場合のメモリ管理ルール:

  • あなたが使用して新しいオブジェクトを作成した場合+alloc/+allocWithZone:+new-copyまたは-mutableCopyかあれば-retain、オブジェクトを、あなたはそれの所有権を取っているし、それが送信されることを確認しなければなりません-release
  • 他の方法でオブジェクトを受け取った場合、そのオブジェクトの所有者ではないため、オブジェクトが送信されたことを確認してはなりませ-release
  • オブジェクトが確実に送信されるようにしたい場合は-release、自分で送信するか、オブジェクトを送信することができます。プール空になる-autoreleaseと、現在の自動解放プールがそれを-release(受信ごとに1回-autorelease)送信します。

-autoreleaseCocoaのイベント処理を囲む自動解放プールがあるため、通常、オブジェクトが現在のイベントの長さにわたって存続することを保証する方法として使用されますが、後でクリーンアップされます。Cocoaでは、自動解放されたオブジェクトを呼び出し元に返す方が、呼び出し元自体が解放する必要があるオブジェクトを返すよりもはるかに一般的です。


6

Objective-Cは参照カウントを使用します。これは、各オブジェクトに参照カウントがあることを意味します。オブジェクトが作成されると、参照カウントは「1」になります。簡単に言えば、オブジェクトが参照される(つまり、どこかに格納される)と、オブジェクトは「保持」され、参照カウントが1つ増えます。オブジェクトが不要になると、「解放」されます。つまり、参照カウントが1つ減ります。

オブジェクトの参照カウントが0の場合、オブジェクトは解放されます。これは基本的な参照カウントです。

一部の言語では、参照は自動的に増減されますが、objective-cはそれらの言語の1つではありません。したがって、プログラマーは保持と解放を担当します。

メソッドを記述する一般的な方法は次のとおりです。

id myVar = [someObject someMessage];
.... do something ....;
[myVar release];
return someValue;

コード内で取得したリソースを解放することを覚えておく必要があるという問題は、面倒でエラーが発生しやすいものです。Objective-Cは、これをより簡単にすることを目的とした別の概念、Autorelease Poolsを導入しています。自動解放プールは、各スレッドにインストールされる特別なオブジェクトです。NSAutoreleasePoolを調べれば、これらはかなり単純なクラスです。

オブジェクトが送信された「autorelease」メッセージを受け取ると、オブジェクトはこの現在のスレッドのスタックにある自動解放プールを探します。オブジェクトをリストに追加して、「解放」メッセージを将来のある時点で送信します。これは通常、プール自体が解放されるときです。

上記のコードを例にとると、次のように記述して、コードを短く読みやすくすることができます。

id myVar = [[someObject someMessage] autorelease];
... do something ...;
return someValue;

オブジェクトは自動解放されるため、「解放」を明示的に呼び出す必要はなくなりました。これは、一部の自動解放プールが後でそれを実行することがわかっているためです。

うまくいけば、これが役立ちます。ウィキペディアの記事は、参照カウントに関してかなり優れています。自動解放プールの詳細については、こちらを参照してください。また、Mac OS X 10.5以降用にビルドする場合は、ガベージコレクションを有効にしてビルドするようにXcodeに指示できるため、保持、解放、自動解放を完全に無視できます。


2
これは間違っています。示されている例のいずれかでsomeObject releaseまたはautorleaseを送信する必要はありません。
mmalc 2008年

6

Joshua(#6591)-Mac OS X 10.5のガベージコレクションはかなりクールに見えますが、iPhoneでは使用できません(または、10.5より前のバージョンのMac OS Xでアプリを実行したい場合)。

また、ライブラリや、再利用される可能性のあるものを作成している場合、GCモードを使用すると、コードを使用しているすべてのユーザーがGCモードも使用するようにロックされます。手動でメモリ。


2
GCと参照カウントの両方をサポートするハイブリッドフレームワークを作成することは完全に可能です。
mmalc 2008年

6

いつものように、人々が参考資料を書き直そうとするとき、彼らはほとんど間違いなく何か間違いをしたり、不完全な説明を提供したりします。

Appleは、Cocoaのメモリ管理プログラミングガイドでCocoaのメモリ管理システムの完全な説明を提供しています。その最後には、メモリ管理ルールの簡潔で正確な概要があります。




2
実はこれは、はるかに優れた単一ページの概要は次のとおりです。developer.apple.com/mac/library/documentation/Cocoa/Conceptual/...
ブライアンMoeskau

6

$ 50を下げてHillegassの本を入手することを検討する場合を除いて、保持/リリースの詳細には追加しませんが、アプリケーションの開発の非常に早い段階でInstrumentsツールを使用することを強くお勧めします(最初の1つ!)。これを行うには、実行->パフォーマンスツールで開始します。入手可能な多くの楽器の1つであるLeaksから始めますが、リリースするのを忘れたときに表示するのに役立ちます。どれだけの情報が提示されるかは気の遠くなるようなものです。しかし、このチュートリアルをチェックして、すぐに始めてください
COCOAチュートリアル:メモリーリークをインストゥルメントで修正する

実際にリークを強制しようとすることは、リークを防ぐ方法を学ぶより良い方法かもしれません!幸運を ;)





4

NilObjectの答えは良い出発点です。手動のメモリ管理に関する補足情報をいくつか示します(iPhoneで必要)。

個人的alloc/initにオブジェクトの場合、参照カウントは1になります。[foo release]またはを呼び出して、不要になったオブジェクトをクリーンアップする必要があります[foo autorelease]。releaseはすぐにクリーンアップしますが、autoreleaseはオブジェクトをautoreleaseプールに追加し、後で自動的に解放します。

autoreleaseは主に、問題のオブジェクトを返す必要があるメソッドがある場合(手動で解放できない場合は、nilオブジェクトを返すことになります)ですが、それを保持したくない場合にも使用します。 。

取得するためにalloc / initを呼び出さなかったオブジェクトを取得した場合-たとえば:

foo = [NSString stringWithString:@"hello"];

しかし、このオブジェクトを使い続けたい場合は、[foo keep]を呼び出す必要があります。それ以外の場合は、取得可能でありautoreleased、nil参照を保持します(上記のstringWithString例のように)。不要になったらに電話してください[foo release]


2

上記の答えは、ドキュメントが言っていることの明確な言い直しを与えます。ほとんどの新しい人々が遭遇する問題は、文書化されていないケースです。例えば:

  • 自動リリース:「将来のある時点で」リリースをトリガーするとドキュメントは言っています。いつ?!基本的に、コードを終了してシステムイベントループに戻るまで、オブジェクトが存在することを期待できます。システムは、現在のイベントサイクルの後、いつでもオブジェクトを解放できます。(マットは以前に言ったと思います。)

  • 静的文字列NSString *foo = @"bar";-それを保持または解放する必要がありますか?いいえ。

    -(void)getBar {
        return @"bar";
    }

    ...

    NSString *foo = [self getBar]; // still no need to retain or release
  • 作成ルール:作成した場合は、それを所有し、リリースすることが期待されています。

一般に、新しいCocoaプログラマーがめちゃくちゃになる方法は、どのルーチンがでオブジェクトを返すかを理解しないことretainCount > 0です。

Cocoaでのメモリ管理の非常に単純なルールのスニペットを次に示します。

保持カウントルール

  • 特定のブロック内で-copy、-alloc、および-retainを使用することは、-releaseおよび-autoreleaseを使用することと同じでなければなりません。
  • 便利なコンストラクタを使用して作成されたオブジェクト(NSStringのstringWithStringなど)は、自動解放されたと見なされます。
  • -deallocメソッドを実装して、所有するインスタンス変数を解放します

最初の箇条書きは言う:(allocまたはnew fooCopy)を呼び出した場合、そのオブジェクトでreleaseを呼び出す必要があります。

2番目の箇条書きは、便利なコンストラクタを使用していて、オブジェクトをぶらぶらする必要がある場合(後で描画されるイメージの場合のように)、それを保持(そして後で解放)する必要があります。

3番目は自明です。


「自動リリース:ドキュメントは、「将来のある時点で」リリースをトリガーすると言っています。いつ?!」ドキュメントはその点で明確です。「自動リリースは、「リリースメッセージを後で送信する」という意味です(後での定義については、「自動リリースプール」を参照してください)。」自動
リリース

...「現在のイベントサイクルの後、システムはいつでもオブジェクトを解放できます。」これにより、システムのサウンドの確定性が実際よりも低くなります...
mmalc

... NSString foo = [self getBar]; //それでも保持または解放する必要はありませんこれは誤りです。getBarを呼び出す人は実装の詳細を知らないため、現在のスコープ外で使用したい場合は*(通常はアクセサ経由で)保持/解放する必要があります。
mmalc 2008

「Cocoaでのメモリ管理のための非常に単純なルール」の記事は、いくつかの点で古くなっています。特に、「便利なコンストラクタ(NSStringのstringWithStringなど)を使用して作成されたオブジェクトは、自動解放されたと見なされます。」不正解です。単に「受信者が所有していない」ということです。
mmalc 2008


0

いくつかの人々がすでに述べたように、メモリ管理入門は、始めるのにはるかに最適な場所です。

まだ言及していない便利なリンクの1つは、実用的なメモリ管理です。読み通せば、Appleのドキュメントの真ん中にありますが、直接リンクする価値はあります。これは、例と一般的な間違いを含むメモリ管理ルールの見事な要約です(基本的に、ここで他の回答が説明しようとしているものですが、説明はありません)。

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