Objective-Cのメモリ管理 このエントリをはてなブックマークに追加

March 11th, 2010

Objective-C のメモリ管理の話をします。Objective-C ではどのようなルールに基づいてコーディングすればよいかを説明します。

  1. alloc, copy, new を送信したオブジェクトは、それによって生成されたオブジェクトを所有します。また、retain を送信したオブジェクトは、その受信側のオブジェクトを所有します。
  2. 所有しているオブジェクトが不要になったら、そのオブジェクトに release メッセージを送信して、所有を放棄しなければなりません。
  3. 所有してないオブジェクトに release メッセージを送信して、そのオブジェクトを放棄しようとしてはいけません。

補足して、次のようなことも頭に入れておきましょう。

  • 誰もオブジェクトを所有しなくなったとき、そのオブジェクトには dealloc メッセージが送信され、そのあとメモリから解放されます。
  • あるオブジェクトを複数のオブジェクトが所有することもあります。
  • あるオブジェクトが特定のオブジェクトを重複して所有することもあります。その場合、重複した所有だけ release メッセージを送信し、所有を放棄する必要があります。
  • 所有してないオブジェクトはどこかで破棄されるので、そのオブジェクトが必要なときは、そのオブジェクトに retain メッセージを送信するか、 copy メソッドを用いて複製し、明示的に所有しなければなりません。

文章ではわかりにくいので、実例をあげて解説を試みます。

例.1 オブジェクトの作成

NSString *string = [[NSString alloc] initWithString:@"abc"];

このメッセージを送信したオブジェクトは、すなわち self は、この string が不要になったら、 string に release メッセージを送信して、オブジェクトを放棄しなければなりません。重要なことは、 self が string を放棄しなければならないことです。これは、最も遅くても self が dealloc されるときに string に release メッセージを送信しなければならないことを意味します。

例.2 簡易コンストラクタの使用

NSString *string = [NSString stringWithString:@"abc"];

このメッセージを送信して作られた string は、alloc, retain, copy, new どれにも当てはまらないので、self は string オブジェクトを所有していません。したがって、 release メッセージを送信して、放棄しようとしてはいけません。逆に考えると、この string は所有していないので、不要でない場合は、明示的に retain して所有しなければなりません。

例.3 コレクションに入れる

NSString *string = [[NSString alloc] init];
NSArray *array = [[NSArray alloc] initWithObject:string];
[string release];

NSArray などのコレクションクラスは、格納されるオブジェクトを明示的に所有します。したがって2行目の時点では、 string は self と array 両者に所有され、array は self に所有されています。string は array だけが所有すればいいので、 self は release メッセージを string に送信して所有を放棄すべきです。このコードの最後の状態は、 self が array を所有し、 array が string を所有している状態になります。

例.4 各種メソッドでオブジェクトを得た

NSURL *URL = [NSURL URLWithString:@"http://www.google.com/"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

まず、登場するオブジェクトを挙げてみましょう。リテラルである @”http://www.google.com/” の NSString オブジェクトを除くと、URL, request, response, error, data, html があります。しかし、最後の html 以外のオブジェクトについて、 self は所有していないので、所有を放棄する必要はありません。したがって、この場合 self が所有しているのは html のみとなり、これが不要になったら所有を放棄する必要があります。

例.5 メソッドの戻り値

- (NSString *)htmlFromURL:(NSURL *)URL {
	NSURL *URL = [NSURL URLWithString:@"http://www.google.com/"];
	NSURLRequest *request = [NSURLRequest requestWithURL:URL];
	NSURLResponse *response;
	NSError *error;
	NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
	NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
	return [html autorelease];
}

例えば、このようなメソッドを考えます。 URL を引数にとり、戻り値にその URL のページを NSString で返します。先の 例.4 をメソッドにしました。ここで問題となるのは、 self が所有してしまっている html を、どうやって放棄すべきかです。 例2. のような、NSString に NSData から文字列にする簡易コンストラクタがあればいいのですが、そのようなメソッドはありません。また、関数内で release してしまうと、html がメモリから破棄される可能性があります。

このような場合の対処法として、 autorelease を使用します。 autorelease については、また別の記事を参照願いたいのですが、 autorelease メッセージを送られたオブジェクトは、あるタイミングで release メッセージが送られます。先にオブジェクトが解放されることなく、所有を放棄することができます。

例6. 簡易コンストラクタの作成

+ (id)classname:(id)anObject {
	return [[[self alloc] init] autorelease];
}

NSArray の array メソッドや、 NSString の stringWithFormat メソッドのように、簡易コンストラクタを自分のクラスでも実装したい場合があります。そのときは、このように alloc 、任意の init メッセージを送信したあとに、 autorelease メッセージも送信することで、所有の放棄されたオブジェクトを返すことができます。

例7. setter の作成

- (void)setObject:(id)anObject {
	if ( anObject != object ) {
		[object release];
		anObject = [object retain];
	}
}

いまやプロパティ構文により必要ない話ですが、setter の中身を知っておくことで、より理解が深まるでしょう。マルチスレッドを気にしなければ、このようなコードになります。なお、インスタンス変数 object にセットする setter です。また、retain で変数を所有していますが、 copy も使う場合もあります。

注意1. retainCount を用いてはならない

参照カウンタ方式のメモリ管理なので、そのオブジェクトのカウント数がいくつなのか気になり、retainCount を見ることがあるかもしれません。しかし、Objective-C において、retainCount は目安でしかありません。したがって retainCount を用いたプログラムを組んではいけません。retainCount は、デバック目的で使用されるべきではありますが、値は参考程度にしましょう。たとえば、次のコードは、どちらも retainCount が 1 ではありません。

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // insert code here...
	NSNumber *num = [[NSNumber alloc] initWithInt:1];
	NSString *s = [[NSString alloc] initWithString:@"ABC"];

	NSLog( @"count of num is %d", [num retainCount] );
	NSLog( @"count of s is %d", [s retainCount] );

	[num release];
	[s release];

    [pool drain];
    return 0;
}

注意2. delegate は所有してはいけない

@property (assgin) id delegate;

もし、delegate となるオブジェクトが必要な場合でも、delegate オブジェクトは所有されるべきではありません。なぜなら、delegate オブジェクトがすでに self を所有していることが多く、循環参照に陥るからです。これは参照カウンタで起きるメモリリークです。


参考資料:
詳解 Objective-C 2.0
Objective-C プログラミング言語 : メモリ管理


関連エントリ:
NSAutoreleasePool

Objective-C逆引きハンドブック このエントリをはてなブックマークに追加

March 6th, 2010

たまたま時間が余ったため、ふらっと書店に立ち寄ると、こんな書籍を見つけました。今まで Windows で開発していたデベロッパーが、サクッと iPhone アプリを開発したいときに、手元にあると非常に役に立ちそうな内容となっています。また、あれをやりたい、これをやりたい、だけど Objective-C ではどうするんだろう? という疑問をすぐに解決してくれるでしょう。

Objective-C逆引きハンドブック Objective-C逆引きハンドブック

シーアンドアール研究所 2010-02-26
売り上げランキング : 5113

Amazonで詳しく見る by G-Tools

おまけとして、こちらの書籍も紹介しておきます。

基本的には、ペンタブレットの開発話や、操作方法などの説明、ペンタブレットを活用できるソフトウェアを紹介している本となります。しかし、一番最後の章に、ペンタブレットを使ったソフトウェア開発の方法が書かれています。Windows では、Win32API, .NET の開発方法、 Mac OS X では、 Cocoa を使った開発方法が書かれています。ペンタブレットを活用したアプリケーションを開発したいデベロッパで、サンプルコードに困っているならば、この本を手に取ってみるのも良いかと思われます。

Intuos HACKS―プロフェッショナル・ペンタブレットの使いこなし術 Intuos HACKS―プロフェッショナル・ペンタブレットの使いこなし術

秀和システム 2010-03-01
売り上げランキング : 79808

Amazonで詳しく見る by G-Tools

ちなみに、 Cocoa の開発方法では、 ペンタブレットの入力は普通に mouseDown: で取得され、 NSEvent から筆圧や傾きの情報を取得できるようです。

WordPress テーマ作成にはこの本 このエントリをはてなブックマークに追加

February 21st, 2010

WordPress のオリジナルテーマを作成したいと思っているあなたに、大変おすすめ出来る本です。本の内容のほとんどは、テーマの作成方法に割かれており、タイトルの通りにステップバイステップ形式でテーマを作成できます。

WordPressレッスンブック 2.8対応―ステップバイステップ形式でマスターできる WordPressレッスンブック 2.8対応―ステップバイステップ形式でマスターできる

ソシム 2009-09
売り上げランキング : 61064

Amazonで詳しく見る by G-Tools

今春はテーマファイルを作成しようと思ったので、思わず買いました。WEB でも探せばいくらでも情報は出てきますし、テーマファイルも多数あるので、それらを参考にすれば本は不要かもしれませんが、読み進めて同じように真似しているだけでテーマが作成できるのは、非常に魅力的だと思いました。

OpenCLの本 このエントリをはてなブックマークに追加

February 5th, 2010

そういえば、OpenCL の本をいくつか見つけたので、紹介しておきます。どちらの本も基本的なことは書かれているような気がしましたが、OpenCL入門 の方が、詳しく載っていた気がします。

OpenCL入門 - マルチコアCPU・GPUのための並列プログラミング - OpenCL入門 – マルチコアCPU・GPUのための並列プログラミング -

インプレスジャパン 2010-01-22
売り上げランキング : 1864

Amazonで詳しく見る by G-Tools

OpenCL並列プログラミング―マルチコアCPU/GPUのための標準フレームワーク OpenCL並列プログラミング―マルチコアCPU/GPUのための標準フレームワーク

カットシステム
売り上げランキング : 38079

Amazonで詳しく見る by G-Tools

ADC にも OpenCL Programming Guide for Mac OS X がありますが、英語で読むのに時間がかかりそうなので、こちらの本と併用していきたいですね。

ドット構文について このエントリをはてなブックマークに追加

February 3rd, 2010

積極的に利用すべきものであるのに、未だに利用していない機能、第三弾、ドット構文です。

これは完全に Objective-C の話です。Objective-C 2.0 からはプロパティ構文が追加され、ドット構文によるアクセサへのアクセスが可能となっています。次のような構文は等価となっています。

// setter
[obj setObject:anObject];
obj.object = anObject;
// getter
id anObject = [obj object];
id anObject = obj.object;

ドット構文を使うことにより、コードがより見やすくなると考えられます。しかしながら、ドット構文を私は使っていません。理由は主に3つあります。

第一に、Objective-C 2.0 は Leopard 以降なので、Leopard 以前を開発の対象とするならば、ドット構文を使用することができないからです。

第二に、私にとっては、ドット構文よりもメッセージ式の方が見やすいと感じるからです。やはり Objective-C 的に考えて、プロパティにドット構文でアクセスするのは邪道な気がしてしまいます。メッセージ式でアクセスしたほうが気持がいいのです。

第三に、ドット構文を利用するためには、そのオブジェクトが静的に解決されている必要があります。id 型に対して、ドット構文は使用できないということです。非常に動的な Objective-C において、より積極的に静的な型を導入させるような構文は、あまり気に入ることができないです。

というわけで、ドット構文を使用していない私です。

Objective-C 2.0プログラミング言語 プロパティ