2013/06/16

[obc]参照カウントを見てみる

さて、allocなんかも使ってみたことだし、噂の参照カウント値を見てみよう。

#import <Foundation/Foundation.h>
#import "1.h"
int main(void)
{
	Hell *hell = [[Hell alloc] init];
	NSLog(@"count = %ld\n", [hell retainCount]);
	[hell print];
	NSLog(@"count = %ld\n", [hell retainCount]);
	[hell release];
	NSLog(@"count = %ld\n", [hell retainCount]);
	
	return 0;
}

printfでもいいけど、そろそろNSLogってのにも慣れておかねば。

$ clang -o tst 1.m main.m -lobjc -framework Cocoa
$ ./tst
+[Hell initialize]
hell init!
2013-06-16 13:30:40.503 tst[791:707] count = 1
hell print : 10
2013-06-16 13:30:40.505 tst[791:707] count = 1
2013-06-16 13:30:40.505 tst[791:707] count = 1

うーん・・・。
releaseしようとすまいと、1だな。。。
gccでもそうだったのでclangにしてみたのだが、それでも同じだ。
(-framework Cocoa、がついたのは、NSLogがリンクエラーになるため。)

ratainすると2になり、次にreleaseすると1になる。
でも、今みたいにretainしなくても、releaseしたら1のままだ。
続けてreleaseすると、落ちる。
うーむ・・・・・・・。

そもそも、デフォルトの場合はARCが有効なのだろうか? 無効なのだろうか?


コンパイルオプションに「-fobjc-arc」をつけるとARC有効、「-fno-objc-arc」をつけるとARC無効。

まずは-fno-objc-arcをつけてみる。。。変化無し。
では、-fobjc-arcをつけると・・・コンパイルエラーだ!

つまり、デフォルトではARC無効でコンパイルされるようだ。

 

エラーの内容は「ARCが有効だとretainCountは参照できんよ」というもの。
あと、retainとreleaseもだめだって。

では、ちょっとソースを変えて試していこう。

//[1.h]
#import <Foundation/Foundation.h>
@interface Hell : NSObject
- (void)print;
@end
--------------------
//[1.m]
#import <stdio.h>
#import "1.h"
static int gValue;
@implementation Hell
//
+ (void)initialize
{
	printf("%s\n", __FUNCTION__);
	if (self == [Hell class]) {
		printf("hell init!\n");
		gValue = 10;
	}
}
- (id)init
{
	printf("%s\n", __FUNCTION__);
	return self;
}
- (void)dealloc
{
	printf("%s\n", __FUNCTION__);
}
//
- (void)print
{
	printf("hell print : %d\n", gValue++);
}
@end
--------------------
//[main.m]
#import <Foundation/Foundation.h>
#import "1.h"
void func(Hell *hell)
{
	[hell print];
	NSLog(@"%s end\n", __FUNCTION__);
}
int main(void)
{
	Hell *hell = [[Hell alloc] init];
	func(hell);
	
	NSLog(@"%s end\n", __FUNCTION__);
	return 0;
}

$ clang -o tst 1.m main.m -lobjc -framework Cocoa -fobjc-arc
$ ./tst
+[Hell initialize]
hell init!
-[Hell init]
hell print : 10
2013-06-16 14:00:20.938 tst[858:707] func end
2013-06-16 14:00:20.940 tst[858:707] main end
-[Hell dealloc]

mainで確保したhellが解放されるのは、mainを抜けたときだ。
では、ちょっと差し替えよう。

//[main.m]
#import <Foundation/Foundation.h>
#import "1.h"
void func(void)
{
	Hell *hell = [[Hell alloc] init];
	[hell print];
	NSLog(@"%s end\n", __FUNCTION__);
}
int main(void)
{
	func();
	
	NSLog(@"%s end\n", __FUNCTION__);
	return 0;
}

$ clang -o tst 1.m main.m -lobjc -framework Cocoa -fobjc-arc
$ ./tst
+[Hell initialize]
hell init!
-[Hell init]
hell print : 10
2013-06-16 14:03:34.217 tst[864:707] func end
-[Hell dealloc]
2013-06-16 14:03:34.218 tst[864:707] main end

funcで確保したhellは、funcを抜けた後で解放される。
じゃあ、これならどうだ?

#import <Foundation/Foundation.h>
#import "1.h"
static Hell *hell;
void func(void)
{
	hell = [[Hell alloc] init];
	[hell print];
	NSLog(@"%s end\n", __FUNCTION__);
}
int main(void)
{
	func();
	
	NSLog(@"%s end\n", __FUNCTION__);
	return 0;
}

$ clang -o tst 1.m main.m -lobjc -framework Cocoa -fobjc-arc
$ ./tst
+[Hell initialize]
hell init!
-[Hell init]
hell print : 10
2013-06-16 14:05:07.479 tst[872:707] func end
2013-06-16 14:05:07.480 tst[872:707] main end

確保はfunc()でやってるけど、変数スコープがグローバルなので解放するタイミングがない。
まあ、これはmain抜けたらプロセスごと終わるからいいんだろうけど、強制的に解放したいときがあるかもしれない。

#import <Foundation/Foundation.h>
#import "1.h"
static Hell *hell;
void func(void)
{
	hell = [[Hell alloc] init];
	[hell print];
	NSLog(@"%s end\n", __FUNCTION__);
}
int main(void)
{
	func();
	
	NSLog(@"%s end\n", __FUNCTION__);
	hell = nil;
	return 0;
}

$ clang -o tst 1.m main.m -lobjc -framework Cocoa -fobjc-arc
$ ./tst
+[Hell initialize]
hell init!
-[Hell init]
hell print : 10
2013-06-16 14:07:28.059 tst[880:707] func end
2013-06-16 14:07:28.060 tst[880:707] main end
-[Hell dealloc]

hellにnilを突っ込むことで結合が切れ、mainを抜けたタイミングで解放される処理が呼ばれた、というところか?
いや、そうではないようだ。
nilを代入する時点で解放が呼び出されていた。
mainのNSLog()前で代入すればそこでdeallocが呼ばれたし、func()で代入すればそこで呼ばれた。
そうなっているんだ、というくらいしか言えることはありませんな。

 

あとは、プロパティとかプロトコルみたいな基本を押さえて、今日は終わりにしますかな。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。