自分でクラスを設計し、インスタンスを生成してみましょう。
自前のクラスを設計して、使ってみる練習をしますよ!
Humanクラスを設計して、和田インスタンスを生成してみます。
まずXcodeから、新規プロジェクトを

このように、Foundation Toolというのを選んでください。
説明を訳すと「このプロジェクトではFoundationライブラリにリンクするコマンドラインツールを作成します」
GUIがないのです。なのでインターフェースビルダーは使いません。
プロジェクト名は「ObjectEX」にしましょう。オブジェクト・エクササイズ。

ObjectEX.mを編集しますー。
すいません先生・・・。和田は今日寝てました・・・。
おはようございますー
じゃあいきなりビルド&実行してみましょう。
どんなんでましたか!
ビルドしましたが何もでてきませんでした…。
コンソールに何か出力されていますよう!
メニューの実行>コンソール
え
どこにあるんすかそれw
あ!あった!
[Session started at 2009-02-24 18:32:01 +0900.]
2009-02-24 18:32:01.655 ObjectEX[411:10b] Hello, World!
The Debugger has exited with status 0.
と表示されました!
Hello, World!が出てますねー
でてますよっ!
らくちんですねこれ!
これは、ソースファイルの、NSLog関数の働きなのです
NSLogは、実際の開発で、デバッグに大活躍しますよー
syslogと同じ感じですかね!
そういえばRSSつきましたよ!
!
すごいっす
ぜひ!
おつかれさまっした!
さて!
Humanクラスを作りますよ

新規ファイル...からこのようにObjective-C classを選んでください。

そして、ファイル名をHuman.mに。.h(ヘッダファイル)も同時に作成。

こんな感じでファイルを整理しておきます。
ちなみにXcode上で見えるグループとファイルでのフォルダ階層は、実際のフォルダ階層とは違ってて

プロジェクトのフォルダをFinderで見ると、ほらこの通り。
Xcode上で見えているファイル構成は、作業がやりやすいようにXcodeが独自に気を利かせてくれているんです。
将来ソースファイルの数が増えてきたときに、自分の好みで新規フォルダ(グループ)を作って整理できますー。
さてHumanクラスの設計です。
名前と年齢を持つことにしましょう。名前は文字列。年齢は整数で。
Human.hを次のようにしましょう。
#import <Foundation/Foundation.h>
@interface Human : NSObject {
int age;
NSString *name;
}
- (int)age;
- (void)setAge:(int)newAge;
- (NSString *)name;
- (void)setName:(NSString *)newName;
- (void)sayHello;
@end
importするものを、CocoaからFoundationに変えています。
Cocoaフレームワーク
は、3つのフレームワークからなっていて、
Foundationフレームワーク AppKitフレームワーク CoreDataフレームワーク、なんです
ためしにspotlightでCocoa.hを探して、開いてみてくださいな。
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import <CoreData/CoreData.h>
ほら、3つimportしてるでしょ。
FoundationはCocoaでもちいる基本的なデータを表現するクラス群。文字列とか配列とか。
AppKitはユーザーインターフェースに関するクラス群。ウインドウとかテキストフィールドとか。
CoreDataはデータ設計を便利にしてくれるクラス群です。
さて今回の練習ではFoundationフレームワークだけでいいわけです。
不要なフレームワークのimportを省くことで、ビルドもちょっと早くなるし、なにより
他の環境へ使い回せる可能性が広がります。
iPhoneではちなみにFoundationフレームワークがそのまま使えるので
今回のHumanクラスは、iPhoneアプリでもそのまま使えます。
なんかうれしいでしょ。
@interface Human : NSObject {
int age;
NSString *name;
}
さてこの{ }の中で、インスタンス変数を宣言しています。
int型(整数)wo
int型(整数)を1つ、NSString(文字列)を1つ。
早起き!
ってか仕事しろって感じですね先生!
あ、おはようございます!
おはようございますw
どんどん進めます!w
おざっす!
ちょっと上から見返してますね!
はーい
Humanを追加してソース書きましたよっ!
やっぱこの形式、わかりやすくていいですね(笑
NSStringのうしろに*があるのは、深い意味がありまして
ポインターですか!?
ええ。変数nameには、文字列インスタンスへのポインタが格納されるっちゅうことなんですよ。
この*,
最初はよく忘れるんですよねw
そしてポインタの意味をよく理解していない和田に説明を…。
えっと、メモリのアドレスのことです
インスタンスが格納されているメモリの先頭アドレス。
文字列インスタンス…へのポインタ…。
ってことはインスタンスはメモリ上に展開されたクラスですよね?
そうですー
つまり展開された後の場所を示すってことですか?
そうですねー
プログラム内でインスタンスを指し示すのは、こういう方法を使うんです。
ふむふむ
わかったようなわからないような…。
とりあえずクラスに対しては、このように*を使うということで、進めましょう!
はい!
- (int)age;
- (void)setAge:(int)newAge;
- (NSString *)name;
- (void)setName:(NSString *)newName;
- (void)sayHello;
あとは、メソッドの宣言です。
全部で5つのメソッドを宣言してみました。
変数もメソッドって言うのですか?
えっと、このageとかnameとかも、メソッドですね
わざと変数と同じ名前にしたメソッド。
!
変数とメソッドが同じ名称ってことですか!?
そうですー
以前、テレビのたとえ話をしましたが
インスタンス変数は、基本的に企業秘密であって、
はい
それを利用者にアクセスさせるかどうかは、このメソッドで決めるわけです
ふむふむ
setAgeとsetNameは、変数を書き換えるメソッドなんですが、
もし利用者に変数を設定してほしくなかったら、このメソッドを宣言しなければいいんです。
ふむぅ?
つまり読み出し専用の変数になるということですね
ユーザエンド側のメソッドとしてsetAgeとsetNameを宣言してるっつーことですよね?
そうです
それゆえポインタが必要になるってことですよね?
どれゆえかしら…
忘れてくださいw
では進めますw
あ!もういっこ!
はいー
@interface Human : NSObject {
int v_age;
NSString *name;
}
- (int)m_age;
こういうのは成立するんですか?
変数とメソッドの名前がちがくてもOKでしょうか?
OKですが
変数と同じ名前のメソッドというのは、実は深い規則がありまして!
!
変数xに対して、メソッドxとsetX
定石!?
があることを前提にした仕掛けが、あるんでやんす
文字列でメソッドを呼び出すしくみがあってね、
なるほど…。了解デス!
退屈なコードをごっそり減らせるありがたいしくみなんですが
でも、どれが変数でどれがメソッドかわからなくなるとかいう心配は無いのでしょうか…。
あ、そうか。いやいや、すいません。
文法上、使い方が違いますよね。
へいw
まだその文法がよく理解できてないのでよくわからなく見えてるのですね和田はw
すぐ慣れると思いますー
ブラジャー!
さてsetAgeとsetNameには、うしろに引数が付いていますね
はい!
(int)newAge
(NSString *)newName
ですね!
いえ違います・・
orz
吹きましたw
自信満々だったのに…orz
あ、そうでしたw
すみませんすみませんw
えええぇえぇええええぇぇぇぇぇ!w
()の中は引数の型で、newなんとかは適当につけた名前です。
これも慣例でメソッドと同じ名称にしてるってわけですねっ!
そうですね
xとかstringとかつけるより、意味がわかるでしょ
はいっ!
で、メソッド名の前にある()には、メソッドの返り値の型を書きます。
int型とNSString型ってことですね
整数と文字列
ええ
ここでもNSStringの後ろに*を忘れないでくださいね。
ポインタが返ってくるということですね。
ポインタが返ってくるということですね。
2回言いましたね。
うふ
ちょっと外出してきまふっ!
はーい
さて今度は実装部です。Human.m
@implementation Human
@end
この2行の間に、メソッドを実装していきます。
まずはageメソッド
- (int)age
{
return age;
}
インスタンス変数ageを返すだけ。
そしてsetAgeメソッド
- (void)setAge:(int)newAge
{
age = newAge;
}
これも簡単で、インスタンス変数ageを、引数newAgeの値に置き換えます。それまでのageの値は消えます。
これでインスタンス変数ageの読み書きができるようになりました。
追加しましたぜ
つぎにnameについて、
- (NSString *)name
{
return name;
}
- (void)setName:(NSString *)newName
{
[newName retain];
[name release];
name = newName;
}
setNameはなんかややこしい!
retainやらreleaseやら、詳しい解説は別テーマでやりましょうかね・・・
retain
ってなんすか
「このインスタンスは俺が面倒を見るぞ!」という感じですね
releaseはその逆。
なんかすごいわかりやすい感じですねw
で、インスタンスはみんな、「何人に面倒を見られているか」カウンタを持っているんです。
参照カウンタと言って、インスタンス変数の1つですね。
ほほぅ!
「誰に面倒を見られているか」は残念ながらわかりません。
プログラマ自身がわからなくなることもありますw
リリースは解放ってことですか?
そうです
retainってなんて読むんですかねぇ
インスタンスが誕生したとき、参照カウンタは1
リテイン、ですね
保持
リゲインみたいですね。
ええ。似た意味ですね
似た意味なんすかw
参照カウンタは、retainされると 1、releaseされると-1
ふむふむ
(あ、プラスが表示されねえ!)
基本的には使った後は解放するんですよね?
そうです
もういらなくなったのに解放されないと、メモリのムダです。
参照カウンタが0になったとき、そのインスタンスは消滅します。
さっきの処理ですが
はい
普通に変数に変数を上書きってわけにはいかんのですか?
[newName retain];
[name release];
name = newName;
いい質問だす!
あら!
単に上書きをするとですね、
もとのnameが指していた文字列インスタンスが、誰からも参照できなくなりますよね。
だから[name release]が必要なんですわ。
なるほど!
ここでポインタさんの話がでてくるってことですね!
そうです
変数に納まっているのは、ポインタなので、
それを意識してもらえばいけると思います!
で、先にnewNameをretainしている理由は、
万が一nameとnewNameが同じ文字列を指していた場合の対策です。
詳しく説明してしまいましたね!
先生!
nameには文字列が入ってるんですよね?
文字列インスタンスのポインタ、ですね
和田憲幸みたいなのが入ってるのではなくて
文字列は、はいってないっす!
あくまでポインタなんですね
そうです。インスタンスを扱うのはいつもこんな感じなんです。
変数型はNSStringなんですか?
そうですね、NSStringクラスですね
ポインタはNSStringに入れるんですか?
NSString *a;
と宣言してから、ani
aに入れます。
ふむふむ
int *a;
でもよいんですか?
それもアリです
aには、「int型の値へのポインタ」が入れられますよ
???????
巷で難解だという評判の、ポインタの話ですねw
その場合の値ってのはどこにあるんですか???
メモリ上の、どこか。
そのアドレスが、aに格納されます。
その値の場所を示すのがポインタ???
直接、変数aに値が入ってちゃ駄目なんですか!?
それはint a;と宣言したときの場合ですね。
*の違いが大きな違いです。
ポインタはどういう場面で使うものなんでしょうか…
とりあえずは、今回の例で、使ってますよね
いまのところはこれだけでいいかとw
Cの本を読むと、もっとすんげえポインタの活用が載ってて、おしっこちびりますけどね
ちびびっ!
さてこれでインスタンス変数ageとnameの読み書きをするメソッドを実装しました!
こういうメソッドを、アクセサメソッドといいますー
accessor
あっ!臭ぇ!サリー!!!!
先生!ガベージコレクションってのは!?
それは、Objective-C 2.0で新登場した機能で、
「誰からも参照されなくなったインスタンスを自動的に見つけて殺してくれる」機能です。
!
この機能をONにしていると、releaseしてやる必要がないっす。
殺し屋???
ええ
しかし、現在iPhone開発環境ではガベージコレクション、使えないんじゃないかなー
変数はいちど展開してしまうと、その変数自体に手を加える事はできないと。
そうおっしゃりたいわけですか
いや、iPhoneアプリ開発では、注意してretain, releaseして面倒を見なきゃ、てことです。
あ…すいません…ポインタの話をしていましたw
PHP、Javascript、Perlで言うとこのローカル変数って事なのかなぁ
ローカル変数・・うーむ・・・
さ、さて、もうちょっとメソッドを追加します!
- (void)sayHello
{
NSLog(@"こんにちは。%@です。%d歳です。よろしくね。", name, age);
}
sayHelloメソッドです。
やっとインスタンス変数の利用例を紹介できましたね。
sayHelloメソッド
っつーのはもう用意されてるメソッドなんですか??
いえ、俺たちのオリジナルですw
.hファイルで、宣言したでしょ
で、ですよねw
このように、好きにメソッドを作りまくるわけです。
ふむふむ
あ!で
これがコンソールに表示されるってことですね!
そうですー
見えてきたかも!
@"..."の文字列が表示されますよ。
@"文字列"
は文法なんですよね?
ええ。これ自身、文字列インスタンスになります。
@を付けないと、伝統的なCの文字列になってしまいます。
普通のC言語は普通に char
Objective-CはCの拡張版なので、Cのソースがそのまま実行できるようになっているんす。
あう…
ふむふむ
たとえば改行は
¥nですか?
そうです
それから、イニシャライザ。
- (id)init
{
[super init];
age = 0;
name = @"no name";
NSLog(@"Humanインスタンスを1つ生成しました。名前は%@、年齢は%d。", name, age);
return self;
}
これは、インスタンスが誕生したときに、最初に呼ばれるメソッドです。
ややこしいですが、これは避けて通れないわよ!
初期設定ですね
ええ
そして、最後にこれ。
javascriptにonloadという似たようなメソッドあります
おお
- (void)dealloc
{
NSLog(@"%@は消滅します。あばよ!", name);
[name release];
[super dealloc];
}
これはインスタンスが消滅する直前に、呼ばれます。
onunloadメソッドですね!
同じ働きのあるんですね
へいっ!
これでHumanクラスは完成です!
人間にはほど遠いですね!
名前と年齢だけですからねえw
けどもうちょっと機能を追加すれば、RPGのキャラクタぐらいにはなるでしょう。
UIがないだけでだいぶ理解しやすい感じがしますね〜
専念できるとわかりやすいですね
さて、いよいよメインルーチンです。これがほんとに最後ですw
!
ObjectEX.mを次のように!
#import <Foundation/Foundation.h>
#import "Human.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Human *wada = [[Human alloc] init]; //和田さん誕生!
[wada sayHello]; //しかし名前はまだない
[wada setAge:20]; //年齢を設定
[wada setName:@"WADA"]; //名前を設定
[wada sayHello]; //ごあいさつ
[wada release]; //和田さん消滅!
[pool drain];
return 0;
}
和田インスタンスの一生です。
ビルドして実行してください!
[Session started at 2009-02-25 15:51:04 +0900.]
2009-02-25 15:51:04.872 ObjectEX[8618:10b] こんにちは。(null)です。0歳です。よろしくね。
2009-02-25 15:51:04.873 ObjectEX[8618:10b] こんにちは。WADAです。20歳です。よろしくね。
The Debugger has exited with status 0.
こんなんでました
あれ、initとdealloc内のNSLogが呼ばれてない…
[Session started at 2009-02-25 15:56:03 +0900.]
2009-02-25 15:56:03.879 ObjectEX[1076:10b] Humanインスタンスを1つ生成しました。名前はno name、年齢は0。
2009-02-25 15:56:03.885 ObjectEX[1076:10b] こんにちは。no nameです。0歳です。よろしくね。
2009-02-25 15:56:03.886 ObjectEX[1076:10b] こんにちは。WADAです。20歳です。よろしくね。
2009-02-25 15:56:03.887 ObjectEX[1076:10b] WADAは消滅します。あばよ!
The Debugger has exited with status 0.
うちではこうなります。
コピー忘れですね…。
メッ!
Humanインスタンス和田の使い方、わかってもらえたでしょうか!
[wada sayHello];
つまり[インスタンス名 メソッド名] ですね
wadaインスタンスに、sayHelloメッセージを送る、といいます
つまり、このwadaインスタンスのsayHelloメソッドが呼び出されるわけですね。
年齢と名前のデータをどのように処理するかは、wadaインスタンスに任せとけばいいんです。
ふむふむ
各インスタンスごとが独立してるってことですよね
独立ってか隔絶?
縦割り行政みたいなもんでしょうか
でポインタという内線をつかって参照することはできまっせと。
そうですねー。役割分担が明確ですね。
今回は、盛り込み過ぎましたかね……
ステップを分けた方が…
オブジェクトを作ろう(1)とかw
むぅ!
説明したいことはまだ山のようにあるのに!
よーしでは続きは別のテーマで!
おざっす!
本日もありがとうございますた!