2013/06/16

[obc]クラスオブジェクト

クラスにひっついている変数を、クラスオブジェクト、と呼ぶそうだ。
今私が読んでいるところでは、クラスオブジェクトといいつつも、クラスの中に入っているわけではない。
staticにした変数は同じファイルの中でしか扱えないから、それを使おう、というしくみみたい。
調べればクラス内に定義した変数でも同じことができるのかもしれんが、まあ後のお楽しみだ。

クラス変数は、+initializeというメソッドで初期化しましょう、ともある。
面倒だから試さないが、きっとこのinitializeもNSObjectを継承しないと使えないのだろう。
では、試しに書いてみる。

//[1.h]
#import <Foundation/Foundation.h>
@interface Hell : NSObject
+ (void)hello;
- (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;
	}
}
//
+ (void)hello
{
	printf("hell hello : %d\n", gValue++);
}
//
- (void)print
{
	printf("hell print : %d\n", gValue++);
}
@end
------------------------------------------
//[main.m]
#import <Foundation/Foundation.h>
#import "1.h"
int main(void)
{
	[Hell hello];
	Hell *hell = [[Hell alloc] init];
	[hell print];
	return 0;
}

今回から、ヘッダファイルも追加したし、mainも別にした。
そうせんとわからんからね。

yebisu:objc hirokuma$ gcc -o tst 1.m main.m -lobjc
yebisu:objc hirokuma$ ./tst
+[Hell initialize]
hell init!
hell hello : 10
hell print : 11

mainではインスタンス生成前に+helloを呼び出しているのだが、それより前に+initializeが呼び出されていることがわかる。
+initializeだが、継承しても呼ばれるとか何とかで、自分のクラスかどうかを判定せにゃならんらしい。

 

では、まず+initializeが1回しか呼ばれないことを確認しよう。

#import <Foundation/Foundation.h>
#import "1.h"
int main(void)
{
	[Hell hello];
	Hell *hell = [[Hell alloc] init];
	Hell *hell2 = [[Hell alloc] init];
	[hell print];
	[hell2 print];
	
	return 0;
}

yebisu:objc hirokuma$ ./tst
+[Hell initialize]
hell init!
hell hello : 10
hell print : 11
hell print : 12

ふむ、よかろう。
では、継承させてみよう。

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

Hellを継承したHell2を作り、あとは全部オーバーライド。

$ gcc -o tst 1.m 2.m main.m -lobjc
$ ./tst
+[Hell initialize]
hell init!
+[Hell2 initialize]
hell init!
hell hello : 20
hell hello : 10
hell print : 11
hell print : 21

+initializeの呼出順は、ベースから順番なのかな?
最初、2.mの+initializeのif文をそのままにしていたので、gValueが初期化されなかった。
この部分の書き換えは忘れやすそうだから、自動的に「今のクラス」を取ってきてくれないものだろうか。

いや、その前に、+initializeが2回呼ばれる、というふうになってない。
どうもそれは、継承先で+initializeをオーバーライドしてないときのようだ。
2.mの+initializeをまるまるコメントアウトすると、こうなった。

$ gcc -o tst 1.m 2.m main.m -lobjc
$ ./tst
+[Hell initialize]
hell init!
+[Hell initialize]
hell hello : 0
hell hello : 10
hell print : 11
hell print : 1

Hell2のときもHell::initializeが呼ばれるけど、そのときはクラス名が違うからif文の中には流れ込まない、と。

となると、やはり書き方をパターン化してしまいたいなあ。
ネットで見ると、初期化済みstatic変数を用意しているところもあったけど、最初の1回だけのためにメモリは使いたくないし・・・。

やりたいことは、@implementationに書いたクラス名を使いたい、ということだ。
そうなると、プリプロセスの段階か。
#defineでクラス名をつけてしまえばいいけど、それはそれでいやだなぁ。

まあ、+initializeを書くことはそう多くないだろうから、注意すればいいか。

0 件のコメント:

コメントを投稿

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

注: コメントを投稿できるのは、このブログのメンバーだけです。