メモリ管理ポリシー
参照カウント環境でのメモリ管理に使用される基本モデルは、NSObject プロトコル で定義されたメソッドと標準的なメソッド命名規則の組み合わせによって提供されます。NSObject クラスは、オブジェクトが割り当て解除されたときに自動的に呼び出される、メソッド dealloc も定義します。この記事では、Cocoa プログラムでメモリを正しく管理するために知っておく必要があるすべての基本的な規則について説明し、正しい使い方の例を示します。
基本的なメモリ管理規則
メモリ管理モデルは、オブジェクトの所有権に基づいています。どのオブジェクトにも 1 人以上の所有者がありえます。オブジェクトに所有者が少なくとも 1 人いる限り、オブジェクトは存在し続けます。オブジェクトに所有者がいない場合、実行時システムは自動的にそれを破棄します。オブジェクトを所有しているときとそうでないときが明確になるように、Cocoa は以下のポリシーを設定します。
- 作成したオブジェクトを所有している
- retain (保持) を使用してオブジェクトの所有権を取得できます
- もはやそれを必要としないときは、所有しているオブジェクトの所有権を放棄しなければなりません
- 所有していないオブジェクトの所有権を放棄してはいけません
名前が "alloc"、"new"、"copy"、または "mutableCopy" で始まるメソッド (例えば alloc、newObject、または mutableCopy など) を使用してオブジェクトを作成します。
受け取ったオブジェクトは、受け取ったメソッド内で有効なままであることが通常保証され、そのメソッドはまたオブジェクトをその呼び出し元に安全に返すこともあります。2 つの状況で retain を使用します。(1) アクセサメソッドまたは init メソッドの実装で、プロパティ値として保存したいオブジェクトの所有権を取得します。(2) 他の操作の副作用としてオブジェクトが無効にならないようにする (使用中のオブジェクトの割り当て解除を避ける に説明されているように)。
オブジェクトの所有権を放棄するには、release メッセージまたは autorelease メッセージを送信します。 したがって、Cocoa の用語では、オブジェクトの所有権を放棄する (relinquish) ことは、通常、オブジェクトを "解放する" (release) と呼ばれます。
これはまさに明示的に述べられている以前のポリシー規則の結果です。
簡単な例
ポリシーを説明するために、以下のコード断片を考えてみましょう。
{ Person *aPerson = [[Person alloc] init]; // ... NSString *name = aPerson.fullName; // ... [aPerson release]; }
Person オブジェクトは alloc メソッドを使用して作成されるため、不要になったときには release メッセージがその後に送信されます。その person の名前は所有メソッドのいずれかをも使用して回収されないので、release メッセージは送信されません。ただし、この例では autorelease ではなく release を使用しています。
autorelease を使用して遅延解放を送信
遅延 (deffered) release メッセージを送信する必要がある場合 (通常はメソッドからオブジェクトを返すとき) には、autorelease を使用します。たとえば、以下のように fullName メソッドを実装できます。
- (NSString *)fullName { NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@", self.firstName, self.lastName] autorelease]; return string; }
あなたは alloc によって返された文字列を所有しています。メモリ管理規則に従うには、参照を失う前に文字列の所有権を放棄しなければなりません。ただし、release を使用すると、文字列は返される前に割り当て解除されます (そして、メソッドは無効なオブジェクトを返します)。autorelease を使用すると、所有権を放棄することを意味しますが、メソッドの呼び出し元は、返された文字列を割り当て解除される前に使用できます。
以下のように fullName メソッドを実装することもできます:
- (NSString *)fullName { NSString *string = [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName]; return string; }
基本的な規則に従って、stringWithFormat: によって返された文字列をあなたは所有していないので、メソッドから文字列を安全に返すことができます。
対照的に、以下の実装は間違っています:
- (NSString *)fullName { NSString *string = [[NSString alloc] initWithFormat:@"%@ %@", self.firstName, self.lastName]; return string; }
命名規則によると、fullName メソッドの呼び出し元は返された文字列を所有していることを示すものは何もありません。したがって、呼び出し元は返された文字列を解放する理由がなく、したがってリークします。
参照で返されたオブジェクトは所有していない
Cocoa のいくつかのメソッドは、オブジェクトが参照によって返されることを指定します (つまり、ClassName ** または id * 型の引数をとります)。一般的なパターンは、initWithContentsOfURL:options:error: (NSData) および initWithContentsOfFile:encoding:error: (NSString) のように、エラーが発生した場合にエラーに関する情報を含む NSError オブジェクトを使用することです。
これらの場合、既に説明したのと同じ規則が適用されます。これらのメソッドのいずれかを呼び出すと、 NSError オブジェクトを作成しないので、そのオブジェクトを所有しません。したがって、この例に示すように、解放する必要はありません。
NSString *fileName = <#Get a file name#> NSError *error; NSString *string = [[NSString alloc] initWithContentsOfFile:fileName encoding:NSUTF8StringEncoding error:&error]; if (string == nil) { // Deal with error... } // ... [string release];
オブジェクトの所有権を放棄するため dealloc の実装
NSObject クラスは、オブジェクトに所有者がなく、そのメモリが再利用されたときに自動的に呼び出される dealloc メソッドを定義します。Cocoa の用語では、これは "解放(freed)" または "割り当て解除(deallocated)" です。dealloc メソッドの役割は、オブジェクトの独自のメモリを解放し、オブジェクトインスタンス変数の所有権を含め、保持するすべてのリソースを廃棄することです。
以下の例は、Person クラスに対して dealloc メソッドを実装する方法を示しています。
@interface Person : NSObject @property (retain) NSString *firstName; @property (retain) NSString *lastName; @property (assign, readonly) NSString *fullName; @end @implementation Person // ... - (void)dealloc [_firstName release]; [_lastName release]; [super dealloc]; } @end
実装の 最後 にスーパークラスの実装を呼び出さなければなりません。
システム・リソースの管理をオブジェクトの寿命に結びつけるべきではありません。dealloc を使って希少リソースを管理しない を参照してください。
アプリケーションが終了しても、オブジェクトは deallocz メッセージを送信しないことがあります。プロセスのメモリは終了時に自動的に消去されるため、すべてのメモリ管理メソッドを呼び出すよりもオペレーティングシステムがリソースをクリーンアップできるようにする方が効率的です。
Core Foundation は、類似するが違う規則を使用
Core Foundation オブジェクトにも同様のメモリ管理規則があります (Core Foundation のためのメモリ管理プログラミングガイド を参照の事)。 しかし、Cocoa と Core Foundation の命名規則は異なります。特に、Core Foundation の 作成規則(作成規則 参照) は、Objective-C オブジェクトを返すメソッドには適用されません。たとえば、以下のコードの断片では、myInstance の所有権を放棄する責任はあなたには ありません。
MyClass *myInstance = [MyClass createInstance];
前の章 次の章