Objective-Cメモリ管理(2)

2011 年 6 月 6 日 by longSlife

前回 Objective-Cのリファレンスカウンタ方式によるメモリ管理について記載しましたが、リファレンスカウンタ方式には、下記のようなオーナシップポリシーがあります。

1) [alloc]又は[new]で始まる名称のメソッド、[copy]を名前に含むメソッドでオブジェクトを生成したものは、所有者となる(所有権を持つ)。

2) 自身で生成していないオブジェクトを所有するには、[retain]を使用し所有権を取得する。

3) オブジェクトの所有(所有権を持っている)者は、所有権が不要になったときに[release] 又は[autorelease]を使用し所有権を放棄(解放)する。
所有権を取得していない使用者は、所有権を放棄(解放)してはいけない。

例えば、メソッド内で例1のようにオブジェクトを生成した場合は、所有権を持っていることになり不要になったときに破棄(解放)する必要があります。

例1:

const unichar message1[] = "Owner String";
NSString* strOwner = [[NSString alloc] initWithCharacters:message1];

また、メソッド内で例2のようにオブジェクトを生成した場合は、所有権を持っていませんので、破棄(解放)を行ってはいけません。
通常受け取ったオブジェクトは、メソッド内では有効であることが保証(マルチスレッド等で例外があります。)されており、一時的な使用の場合所有権の取得も必要ありません。

例2:

const unichar message2[] = "Tempolary String";
NSString* strTemporary = [NSString stringWithCharacters:message2];

例1のように所有権が発生する方法でオブジェクトを生成、メソッドの呼び元にオブジェクトを渡し、所有権を破棄したい場合どうすればいいのでしょうか。
その場合は、例3のように破棄(解放)を予約し実際の破棄(解放)を遅らせてオブジェクトを渡します。

例3

- (id)hoge{
const unichar message1[] = "Owner String";
NSString* strOwner = [[NSString alloc] initWithCharacters:message1];
[strOwner autorelease]; //所有権の破棄(解放)予約
return strOwner;
}

所有権の破棄(解放)予約は、NSAutoreleasePoolオブジェクトに対して行われます。
そのため、所有権の破棄(解放)予約を行う場合、NSAutoreleasePoolオブジェクトが1つ以上生成されている必要があり、NSAutoreleasePoolオブジェクトに予約したオブジェクトの破棄(解放)は、NSAutoreleasePool破棄時に実施されます。
通常Xcodeでプロジェクトを作成すると「main.m」内でNSAutoreleasePool生成ー破棄が行われており、autoreleaseは、使用できる状態にあります。

前回のコードに例4の修正を行い実行してみました。

例4:OTester.h

@interface OTester : NSObject
{
NSString* strOwner;
NSString* strTemporary;
}
- (id)initWithCString:(const char*) cStr;
- (void)print;

@end

例4:OTester.m

@implementation OTester
..
/**
*@brief : 初期化
*/
- (id)initWithCString:(const char*) cStr
{
if([super init] != nil){
strOwner = [[NSString alloc] initWithCString:cStr
encoding:NSUTF8StringEncoding];
strTemporary = [NSString stringWithCString:cStr
encoding:NSUTF8StringEncoding];
}
NSLog(@"init %d", (int)[self retainCount]);
return self;
}

/**
*@brief:メンバ変数のLog出力
*/
-(void)print
{
NSLog(@"Owner:%@ %d", strOwner, [strOwner retainCount]);
NSLog(@"Temporary:%@ %d", strTemporary, [strTemporary retainCount]);
}
...
@end

例4:Testmain.m

/**
*@brief : テストプログラム Main
*/
int main()
{
id testObj;
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
printf("---- Memory Manage Test Main Start -----\n");
//
NSAutoreleasePool* pool2 = [[NSAutoreleasePool alloc] init];
testObj = createTestObj();
[testObj retain];
[testObj print];
[pool2 release]; //testObjのstrTemporaryは破棄されるはず。
[testObj print];
[testObj autorelease];
printf("---- Memory Manage Test Main End -----\n");
[pool release];
return 0;
}

実行結果

---- Memory Manage Test Main Start -----
2011-05-29 23:32:03.055 OTester.out[380:903] alloc 1
2011-05-29 23:32:03.058 OTester.out[380:903] init 1
2011-05-29 23:32:03.059 OTester.out[380:903] retain 2
2011-05-29 23:32:03.060 OTester.out[380:903] Owner:C String define 1
2011-05-29 23:32:03.060 OTester.out[380:903] Temporary:C String define 1
2011-05-29 23:32:03.061 OTester.out[380:903] release st 2
2011-05-29 23:32:03.061 OTester.out[380:903] release ed 1
2011-05-29 23:32:03.062 OTester.out[380:903] Owner:C String define 1
2011-05-29 23:32:03.062 OTester.out[380:903] Temporary:Owner:C String define 1 -1
---- Memory Manage Test Main End -----
2011-05-29 23:32:03.063 OTester.out[380:903] release st 1
2011-05-29 23:32:03.063 OTester.out[380:903] dealloc 1
2011-05-29 23:32:03.064 OTester.out[380:903] release ed 1

pool2破棄後の[testObj print];は、実行時エラーになっていませんが「Temporary:Owner:C String define 1 -1」となり、メモリ内容が壊れ、allocを使用せずに生成したオブジェクトは、AutoreleasePoolに破棄(解放)予約され破棄されたと考えられます。
また、「printf(“—- Memory Manage Test Main End —–\n”);」後に行っている[pool release];によりtestObjが破棄(解放)されています。

つづく

タグ: ,

TrackBack