こんにちは。
金琪峰(キム ギボン)と申します。
最近アプリのメモリー管理について悩んでいる方がいますので。
私が知っていることを説明します。
100%信じないでください。
でも、メモリー管理の概念は正しいと思いますので
これを読む方に役に立って欲しいです。:)
iOS プログラマーなら一番面倒くさいと思うことがメモリー管理だと思います。
このメモリー管理について話します。
Javaプログラミングをした方は
Garbage Collectionが自動的にメモリー管理をしてくれるので
初めてiOSを開発した時たくさん荒てたと思いますが。
Apple社はエンジニア(開発者)が直接メモリー管理をするようにわざわざ誘っています。
理由はモバイルという環境のせいです。
デスクトップみたい良い環境の場合には几帳面にメモリー管理をしなくても大きい問題にならないですが。
モバイル端末の場合は
最適化されたメモリー管理によってアプリの動作が完全違います。
それでApple社はわざわざメモリー管理を開発者がするように誘っています。(強要しています!!)
1)一旦メモリー管理の基本的な概念は
retainと releaseで管理が成り立ちます。
Objectを振り当てたら retainCountを +1 になります。
使い終わったObjectに対して releaseをしたら retainCountが -1になって
総retainCountが0になったら自動的にObjectが消えます。
例)
ClassA *obj = [[ClassA alloc] init];
...
[obj release];
正確にどの時点に retainCountをチェックして Objectを解体するかは後で説明します。
じゃ、retain/ releaseのおかげで(?)
開発者は自分が生成した 全てのObjectに対して使い終わった時点で必ずreleaseをしなければいけないです。
簡単に少ないObjectだけ使うプロジェクトなら良いですが
Classファイルが100~1000個ぐらい大きいプロジェクトに一々メモリー管理をすること想像して見てください。 >_<
それで。。。
2)Apple社で提供をするのがautoreleaseと言う奴があります。
何か自動的にメモリーを管理する奴みたいです。
そうですが、自動的なメモリー管理は50%だけです。
なぜならいつ自動的にreleaseをするかのを分からないので
急にアプリがクラッシュになる可能性があります。
私が経験したところによれば80%は問題ありませんが20%自分が予想出来なかった時点でクラッシュになります。
autoreleaseのせいで勝手に消えたObjectを参照しようとしてクラッシュになります。
そしてもう必要がないObjectをずっと持っているようになってしまうので, これも非效率的です.
それじゃこの autoreleaseは一体いつ使用をするのか? 疑問です。ー_ー?
このautoreleaseという概念は内部で Objectを生成して使い終える時使います。
例えば
TestMethodと言うMethodがあると仮定します。
こいつはMethodの内でNSArrayを生成して, このNSArrayを返還する役目をします。
もしNSArrayをautoreleaseかけなくて生成した場合はメモリリークになってしまうのでずっと気を使ってくれなければならないはずです。
開発者立場ですごく面倒になります。
それで普通こんな場合にはTestMethod内で return Objをする時 autoreleaseをかけておいたら
これ以来には変換したarrayについて気を使わなくても良いです。
例)
- (NSArray*)TestMethod{
NSArray *arry = [[NSArray alloc] init];
….
return [array autorelease]; //あらかじめかけておいても良いです.
}
こんな感じです。
この TestMethodを呼び出した Object内ではこの autoreleaseがかけているarrayをどうやって使うか悩みます。
なぜなら
autoreleaseがかけていたら, いつかObjectが解体されるかも知れないからです。
解決方法はarrayに retainをかけて使うことです。
つまり, これからarrayはA Object自分が管理をすると言うことと同じです。
それでは開発者立場では気を使う領域が減ってメモリー管理をするのがもっと易しくなります。
簡単にObject刑を受け取る時retainをかけて使って使い終わったらreleaseをかけたらおわりからです。
例)
NSArray *tmpArray = [[self TestMethod] retain];
つまり、autoreleaseは特定 Objectに retainCountを管理する権利を委任する時使われると思えば良いです。
でも、iOSを開発する時
[[Object alloc] init];
でObjectを生成しなくて使う場合があります。
例えば
NSArray *array = [NSArray array];
NSDictionary *dic = [NSDictionary dictionary];
NSString *str = [NSString stringWithFormat:@"%@", str];
等。。たくさんあります。
上のObjectたちを使い終えてreleaseをかけてくれた時アプリがクラッシュになったことがあると思います。
どうしてクラッシュになるか。
理由は内部的にautorelease処理をするからです。
内部的にautorelease処理になっているObjectをreleaseをしたので
後でautorelease処理をする時
[nil release]をするようになります。(=_=;;
autoreleaseはここまでです。
3)最後に Propertyのメモリー管理があります。
Javaの setter/ getter のような概念です。
他のObjectからinstanceを参照する時使いますが。
このObject instanceにretainをかける場合があります!!
下記のように宣言して使います。
@property (nonatomic, retain) NSMutableArray *tmpArray;
この retain オプションが問題です。
例)
xxx.tmpArray = other_Array;
これに使う時
内部の処理を見たら
if(tmpArray)[tmpArray release];
tmpArray = [other_Array retain];
return tmpArray;
になります.
内部的に retainをかけます。
これが問題です!!
retainが内部的に自動でかけているので
propertyのオプションにretainのかけたら必ずrelease処理をしなければいけないです。
だったらいつ処理するか?
それは
クラスのObjectが解体される時
- (void)dealloc{
[obj1 release];
...
}
-(void)dealloc Methodが呼び出しになります。
- (void)dealloc で release処理をすれば良いです。
今まで
1) retain/ release
2) autorelease
3) property
について説明しました。
今からは
内部的に隠された retain/release管理に対してもっと調べます。
先に NSArrayのような配列にObjectを入れる場合があります。
例)
[tmpArray addObject:obj];
この時内部的にretainがかけられます。
もっと説明したら
AObject *obj = [[AObject alloc] init]; // retainCount +1
NSMutableArray *tmpArray = [NSMutableArray array];
[tmpArray addObject:obj]; // retainCount +1
合わせてretainCountが2になります。
そして後でreleaseをしても-1だけなのでObjectが解体されないです。
結局メモリーリークになります。
それで配列にObjectを入れる場合には特別な場合を除き
autorelease 処理をして入れます。
もちろんDictionaryの場合も同じです。
##応用編##
iOS開発本を見れば
[self.view addSubview:tmpView];
[tmpView release]
こんなrelease処理ををよく見られます。
これが何を意味しましょうか?
iOSは UIViewを管理する時 Arrayの概念で管理します。
つまり、一番初めに addSubViewになるViewが 0 indexの ViewのArrayに入って行きます。
このUIViewに関するArrayが self.view.subviews です。
NSArrayで addObjectになる時内部的に retainがかけるのを理解している方なら
この後どうして releaseをしてくれるのかも理解できると思います。
文が長くなりました。
長い内容読んでくださってありがとうございます。