自動解放プールブロックの使用
Autorelease (自動開放) プールブロックは、オブジェクトの所有権をそれで放棄できますが、メソッドからオブジェクトを返すなどのように、すぐに割り当て解除する可能性を回避するためのメカニズムを提供します。通常は、独自の自動解放プールブロックを作成する必要はありませんが、そうする必要がある場合や、有益な場合があります。
自動開放プールブロックについて
以下の例に示すように、自動解放プールブロックには @autoreleasepool を使用してマークを付けます。
@autoreleasepool { // Code that creates autoreleased objects. }
自動解放プールブロックの最後に、ブロック内で autorelease メッセージを受信したオブジェクトに release メッセージが送信されます。オブジェクトは、ブロック内で autorelease メッセージが送信されるたびに release メッセージを受信します。
他のコードブロックと同様に、autoreleaser プールブロックは入れ子にすることができます:
@autoreleasepool { // . . . @autoreleasepool { // . . . } . . . }
(通常は、上記のようにコードはまったく表示されません。あるソースファイル内の autorelease プールブロック内のコードは、別の autorelease プールブロックに含まれる別のソースファイル内のコードを呼び出します)。特定の autorelease メッセージの場合、autorelease メッセージが送信された autorelease プールブロックの最後に、対応する release メッセージが送信されます。
Cocoa は、autorelease プールブロック内でコードが実行されることを常に期待しています。そうしないと、自動解放されたオブジェクトは解放されず、アプリケーションはメモリをリークします。(autorelease プールブロックの外で autorelease メッセージを送信すると、Cocoa は適切なエラーメッセージを記録します)。AppKit および UIKit フレームワークは、autorelease プールブロック内の各イベントループ反復処理 (マウスダウンイベントやタップなど) を処理します。したがって、あなたは通常、autorelease プールブロックを自分で作成する必要はなく、作成するコードを参照する必要もありません。しかし、あなた自身の autprelease プールブロックを使うかもしれない 3 つの機会があります:
- コマンドラインツールなど、UI フレームワークに基づいていないプログラムを書いている場合。
- 多くの一時的なオブジェクトを作成するループを書く場合。
ループ内で autorelease プールブロックを使用して、次の反復の前にそれらのオブジェクトを破棄することができます。ループ内で autorelease プールブロックを使用すると、アプリケーションの最大メモリ占有量を削減できます。 - 二次スレッドを生成する場合。
スレッドが実行を開始するとすぐに独自の autorelease プールブロックを作成しなければなりません。そうしないと、アプリケーションはオブジェクトをリークします。 詳細については、自動解放プールブロックとスレッド を参照してください。
ローカルの自動開放プールブロックを使用しピークメモリ使用量を削減
多くのプログラムは、自動解放される一時オブジェクトを作成します。これらのオブジェクトは、ブロックの最後までプログラムメモリの使用量に追加されます。多くの状況では、現在のイベントループの終了が過度のオーバーヘッドにならないように一時オブジェクトを蓄積できるようにします。ただし、状況によっては、一時的なオブジェクトが多数作成し、メモリの使用量に実質的に追加し、より迅速に廃棄したい場合があります。後者の場合は、独自の自動解放プールブロックを作成できます。ブロックの終わりに、一時オブジェクトが解放され、その結果通常は割り当てが解除され、プログラムメモリの使用量が削減されます。
以下の例は、for ループでローカルの autorelease プールブロックを使用する方法を示しています。
NSArray *urls = <# An array of file URLs #> for (NSURL *url in urls) { @autoreleasepool { NSError *error; NSString *fileContents = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error]; /* Process the string, creating and autoreleasing more objects. */ } }
for ループは、一度に 1 つのファイルを処理します。autorelease プールブロック内の autorelease メッセージを送信したオブジェクト (fileContents など) は、ブロックの最後に解放されます。
自動解放プールブロックの後、ブロック内で自動解放されたオブジェクトを "廃棄済み" とみなす必要があります。そのオブジェクトにメッセージを送信したり、メソッドの呼び出し元に戻したりしないでください。autorelease プールブロックを超えて一時オブジェクトを使用しなければならない場合は、ブロック内のオブジェクトに retain メッセージを送信し、ブロックの後に autorelease を送信して行うことができます。以下の例を参照の事。
– (id)findMatchingObject:(id)anObject { id match; while (match == nil) { @autoreleasepool { /* Do a search that creates a lot of temporary objects. */ match = [self expensiveSearchForObject:anObject]; if (match != nil) { [match retain]; /* Keep match around. */ } } } return [match autorelease]; /* Let match go and return it. */ }
autorelease プールブロック内で match に retain を送信すると、autorelease プールブロックが match の寿命を延長した後に autorelease を送信し、ループの外のメッセージを受信して findMatchingObject: の呼び出し側に返すことができます。
自動解放プールブロックとスレッド
Cocoa アプリケーション内の各スレッドは、独自の自動解放プールブロックのスタックを維持します。Foundation だけのプログラムを書いている場合、またはスレッドを切り離す場合は、独自の自動解放プールブロックを作成する必要があります。
あなたのアプリケーションやスレッドが長期間存続し、潜在的に自動解放されたオブジェクト多くを生成する場合は、自動開放プールブロック (メインスレッド上で AppKit や UIKit などがするようにう) を使用する必要があります。それ以外の場合は、自動解放されたオブジェクトは蓄積し、メモリ使用量が増加します。切り離されたスレッドが Cocoa を呼び出さない場合は、自動解放プールブロックを使用する必要はありません。
前の章 次の章