2. 一番始めの Objective-C プログラム

クラスの宣言と実装でファイルを2つ作る

ソースファイルの構造は:

--------------
1. Song.h
2. Song.m
3. Singer.h
4. Singer.m
5. main.m
6. GNUmakefile
--------------

図に示すと,

_images/objc0201.gif

上記の図では, Singer.h で Song.h の読み込みが指示され, さらに main.m では Song.h と Singer.h の両方の読み込みが指示されているので, main.m において Song.h が 2 度読み込まれているように見えるが, #import では同一のヘッダファイルが 2 重に読み込まれることはない.

  1. Song.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>

// Song クラスの宣言
@interface Song : NSObject{
  NSString *lyrics;
}

- (NSString *) lyrics;
- (void) setLyrics: (NSString *)argLyrics;

@end
  1. Song.m
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#import "Song.h"

// Song クラスの実装
@implementation Song : NSObject

- (NSString *) lyrics{
  return lyrics;
}

- (void) setLyrics: (NSString *)argLyrics{
  lyrics = argLyrics;
}

@end
  1. Singer.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#import <Foundation/NSObject.h>
#import <stdio.h>
#import "Song.h"

// Singer クラスの宣言
@interface Singer : NSObject{
  Song *song;
}

- (void) setSong: (Song *)argSong;
- (void) sing;

@end
  1. Singer.m
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#import "Singer.h"

// Singer クラスの実装
@implementation Singer : NSObject

- (void) setSong: (Song *)argSong{
  song = argSong;
}
- (void) sing{
  printf( "the song is: %s\n", [[song lyrics] UTF8String] );
}

@end
  1. main.m
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#import <Foundation/Foundation.h>
#import "Song.h"
#import "Singer.h"

// 実行プログラム
int main(int argc, char *argv[]){

  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  
  id song;
  id singer;

  song = [[Song alloc] init];
  [song setLyrics:@"La La La ..."];

  singer = [[Singer alloc] init];
  [singer setSong:song];
  [singer sing];

  [pool drain];

  return 0;
}
  1. GNUmakefile
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
.SUFFIXES: .o .m
.m.o:
	$(CC) -c $(CFLAGS) $<

PROG = main
CC = gcc
CFLAGS = -g -Wall
FRAMEWORKS = -framework Foundation
SRC = Song.m Singer.m main.m
OBJ = Song.o Singer.o main.o

hist: $(OBJ)
	$(CC) $(CFLAGS) -o $(PROG) $(OBJ) $(FRAMEWORKS)

.PHONY: clean
clean:
	$(RM) $(PROG) $(OBJ) *~ a.out

Song.o: Song.h Song.m
Singer.o: Singer.h Singer.m
main.o: Song.h Singer.h main.m

main.m の実行結果は:

[wtopia 2]$ ./main
the song is: La La La ...

クラス名とスーパークラス名の宣言

クラス名は, @interface に続けて:

クラス名 : スーパークラス名

のように記述する.

通常, あらゆるクラスがこの NSObject クラスの直接または間接的なサブクラスとして定義されるため, NSObject クラスはルートクラスとも呼ばれる.

メンバ変数の宣言

クラス型の変数は, いわゆるポインタとして宣言する必要がある.

ポインタは, クラスの実体が保存されているメモリ上のアドレスを表す変数で, 変数名の前に * を付けて宣言する.

クラス型の変数が常にポインタであることで, プログラム内での変数の受け渡し時に, 常に同じ実体を指し示すことができる.

メソッドの宣言

Objective-C におけるメソッドの記述方法は独特で, この言語の大きな特徴でもある.

先頭の - に続く () 内には, メソッドの戻り値の型 (なければ, void にする), 続いてメソッド名を記述する. 引数がある場合には, 続けて : (コロン), 引数の型 (これも () 内に記述), 後は引数名のように記述する.

メソッドの実装

Song クラスの lyrics メソッドや setLyrics メソッド, および Singer クラスの setSong メソッドは, 単にクラスのメンバ変数の値を取得したり, 新たに値をセットしたりするだけのもの. このようなメソッドを, 変数に対するアクセサト呼ぶ.

id 型の変数を用意

クラスのオブジェクトを保存するために, 汎用的な型である id 型の変数を用意する. id 型の場合は * でポインタであることを明示する必要がない.

クラスのインスタンスを作成

alloc と init は, あるクラスの実体を生成して初期化するための定番メソッドで, 両メソッドとも, ルートクラスである NSObject に定義されているので, 自作のクラスで独自に定義していなくても利用できる.

メッセージ式は, Song クラスや Singer クラスに対して alloc メソッドを実行するようメッセージを送り, さらにその戻り値であるクラスの実体に対して init メソッドを実行するようにメッセージを送る.

コンパイル, リンク, そして実行

_images/objc0202.gif