Objective-Cを触ってみんとす。

iPhoneSDKが公開されたので、とりあえず ObjectiveC の勉強をしてみんとする。
といっても、Macももってないし、iPhone SDK を入手するわけでもなく。

とりあえず、MinGWWindows版で試してみる。

クラス定義

  • クラスを定義するには、@interface と、@implementationが必要。
    • @interfaceがクラスのインタフェースの宣言。
    • @implementationが実装クラス。
  • インスタンスメソッドは、"-" クラスメソッドは、"+"
  • 自クラス内のメソッドを呼び出すときは、self


とりあえず、以下のとおりに、Pointクラスを作ってみた。
参考にしたのは、http://wisdom.sakura.ne.jp/programming/objc/objc4.html
の内容。

// Point.h
#import <objc/Object.h>

@interface Point : Object
{
	// instance variables
	int x; 
	int y;
}
- (void)x: (int)value;
- (int)x;
- (void)y: (int)value;
- (int)y;
- (void)x: (int)valueX y:(int)valueY;
- (int)methodA;
- (int)methodB;
@end
// Point.m
#import "Point.h"

@implementation Point
- (void)x: (int)x {	self->x = x; }
- (int)x { return x + 10; }
- (void)y: (int)y {	self->y = y; }
- (int)y { return y; }
- (void)x: (int)x y:(int)y {
	self->x = x;
	self->y = y;
}
- (int)methodA {
	return x + y;
}
- (int)methodB {
	return [self methodA] * 2;
}
@end
// PointMain.m
#import <stdio.h>
#import <objc/Object.h>
#import "Point.h"

int main() {
	id p1, p2;
	
	p1 = [Point alloc];
	p2 = [Point alloc];
	
	[p1 x:1 y:2];
	[p2 x:3 y:4];
	
	printf("p1 x:%d y:%d\n", [p1 x], [p1 y]);
	printf("p2 x:%d y:%d\n", [p2 x], [p2 y]);
	
	[p1 x: 102];
	[p2 y: 500];

	printf("p1 x:%d y:%d\n", [p1 x], [p1 y]);
	printf("p2 x:%d y:%d\n", [p2 x], [p2 y]);
	
	return 0;
}

参考にしたサイトだと、アクセッサをsetPoint とか、getX とか定義していたんだけど、
set とか get とか個人的にいやなので、インスタンス変数と同じ名前で、アクセッサを用意してみた。
Objective-C界隈では、どんな慣習になっているのか分からないけど。

これを、実行すると、

>gcc -c -Wno-import Point.m
>gcc -c -Wno-import PointMain.m
>gcc -o Point Point.o PointMain.o -lobjc
>Point.exe
p1 x:1 y:2
p2 x:3 y:4
p1 x:102 y:2
p2 x:3 y:500

気になるのが、インスタンス変数の宣言が、@interface 側にあること。
インスタンス変数は実装に応じて用意するものだと思っているので、
@implementation側はわかるとして、@interface側はしっくりこない。

@interfaceは、実装もある程度規定するという意味なんだろうか。
Javaのinterfaceとは、考え方が違うものなのかな。。

継承

// SuperClass.h
#import <objc/Object.h>

@interface SuperClass : Object
{
	int x;
}
-(void)methodA;
-(void)methodC;
-(void)methodD;
@end

// SuperClass.m
#import "SuperClass.h"

@implementation SuperClass
-(void)methodA {
	printf("SuperClass methodA.\n");
}
-(void)methodC {
	printf("SuperClass methodC.\n");
}
-(void)methodD {
	printf("SuperClass methodD.\n");
}
@end

//SubClass.h
#import "SuperClass.h"

@interface SubClass : SuperClass
-(void)methodB;
-(void)methodC;
-(void)methodD;
@end

//SubClass.m
#import "SubClass.h"

@implementation SubClass
-(void)methodB {
	printf("SubClass methodB.\n");
}
-(void)methodC {
	printf("SubClass methodC.\n");
}
-(void)methodD {
	printf("SubClass methodD.\n");
	[super methodD];
}
@end

//SuperClassMain.m
#import <stdio.h>
#import "SubClass.h"


void callMethodC(id obj) {
	[obj methodC];
}

int main() {
	id obj = [SubClass alloc];
	[obj methodA];
	[obj methodB];
	[obj methodC];
	callMethodC(obj);
	[obj methodD];
	obj = [SuperClass alloc];
	[obj methodA];
	[obj methodC];
	callMethodC(obj);
	return 0;
}

実行すると、

> gcc -c -Wno-import SuperClass.m
> gcc -c -Wno-import SubClass.m
> gcc -c -Wno-import SuperClassMain.m
> gcc -o SuperClass SuperClass.o SubClass.o SuperClassMain.o -lobjc
> SuperClass
SuperClass methodA.
SubClass methodB.
SubClass methodC.
SubClass methodC.
SubClass methodD.
SuperClass methodD.
SuperClass methodA.
SuperClass methodC.
SuperClass methodC.

なるほど、肝はcallMethodC関数の中。

コンストラクタ(イニシャライザ)

  • (id)initを定義する。
    • [ClassName new] でインスタンスを作成できる。
    • [[ alloc] init] を呼び出すのと同じ。
  • 引数つきコンストラクタは、initWithではじめるのが慣習らしい。
    • initWith だと、new は使えない。 [[ alloc] initWith: hoge]

ひとつひとつかいてもしょうがないので。

  • allocしたメモリは、freeメソッドで開放してあげないとならない。
    • freeをオーバライドして、デストラクタとする。
  • id型でなく、クラス型のポインタを使って、型をコンパイル時に判定できる。
    • HogeClass *obj = [[HogeClass alloc] init]; [obj method];
    • けど、所詮ポインタなので、実際はどのクラスでも代入可能。
  • クラス変数はない。
    • クラスのヘッダファイルの中に、ファイルスコープのグローバル変数を定義する。
  • クラスもオブジェクト
    • Class testClass = [Test class];
    • [testClass Write];
    • [[testClass new] free];

あしたはセレクタから。