About
About Objective-C Dev Guide
 
Objective-Cの基礎

初めてのCocoaアプリケーション、「Hello World」を作ってみる」では、実際にソースコードを記述し、簡単なアプリケーションを作りました。今回は、前回作成した「Hello World」アプリケーションを使い、「Hello World」アプリケーションのソースコードの詳細と、Objective-Cの基礎を解説します。

クラスの継承関係
まずはヘッダファイルから解説していきます。Objective-Cでは既に存在しているクラスの機能を引き継いでクラスを生成する拡張機能が提供されているのでした。それをソースコードで明示しているのが先ほどの「@interface」と書かれた部分です。
 
@interface AppController : NSObject
この一文は、先ほど作成した「AppController」クラスが「NSObject」というクラスを継承して作られている事を表しています。たとえば、ウィンドウを扱うクラス、「NSWindow」クラスを継承したい場合、
 
@interface AppController : NSWindow
と記述することで、NSWindowクラスを拡張した新しいクラスの生成が可能です。特にクラスの拡張を行わない場合は「NSObject」クラスを指定しておきましょう。今作成しているクラスの継承関係を示すこの一連の作業のことをクラスの宣言と呼びます。

サブクラスが継承しているクラスの元をたどっていけば、最終的にそれ以上の継承関係がない大元のクラスが存在しています。この大元のクラスのことをルートクラスと呼び、先ほどのクラス宣言で指定した「NSObject」がそのルートクラスにあたります。クラスの拡張を行わない場合であっても、クラスの宣言は必ず行わなければなりません。「まっさらな状態のクラスを作るにはNSObjectを継承する」と覚えておきましょう。

クラスの継承関係を表すと、図のようになります。「サブクラス1」と「サブクラス2」はNSObjectクラスを継承しているので、NSObjectクラスのメソッドをそのまま使え、必要に応じてそのメソッドをオーバーライドすることができます。また、その下層にある赤色のクラス群は緑色のクラスを継承しているので、親クラスである「サブクラス」が定めるメソッドの呼び出し、オーバーライドができます。ここでワンポイントですが、継承関係はツリー構造になっているので、赤色のクラス群は親クラスのさらに親クラスをたどり、ルートクラスである「NSObject」のメソッドも使うことができるのです。
ただし、この継承関係では常に「どの位置にクラスを定義するのか」を意識しなければなりません。たとえば、「サブクラス2-1」、及び「サブクラス2-2」は「サブクラス1」のメソッドは使えません。これは「サブクラス1」を継承していないためです。同様に、「NSObject」クラスを継承して新しいクラスを作成した場合、たどることができる親クラスは「NSObject」しかない為、「サブクラス1」と「サブクラス2」のメソッドは使えない(NSObjectしか使えない)という事になります。何らかのクラスを継承してクラスを作成する際は、この継承関係には特に注意しましょう。

フレームワークとクラスのインポート
アプリケーションを記述していく上で、まず始めにフレームワークを読み込んでおかなければそのフレームワークの機能を呼び出せません。以下はヘッダファイルの一行目にあったコードです。
 
#import <Cocoa/Cocoa.h>
この一行ではCocoaのフレームワークを読み込んでいることを表しています。このように、ソースコード上でフレームワークを読み込むことをインポートと言います。

ちなみに、インポートの作業ではフレームワークではなくクラスもインポートする事ができます。先ほどの「クラスの継承関係」では、「サブクラス1」からは「サブクラス2」のメソッドを使うができないと言いました。このインポート文を使えば、「サブクラス1」から「サブクラス2」のメソッドを使うことができます。ただし、継承関係でなければメソッドのオーバーライドを行うことはできないので、あくまでも「拡張」ではなく「参照」になります。「サブクラス1」から「サブクラス2」をインポートしたい場合はサブクラス1のヘッダーファイルを以下のように追加します。
 
#import <Cocoa/Cocoa.h>
#import "サブクラス2.h"   //追加した行
インポートしたいクラスがプロジェクト内にある場合は、ヘッダファイルの名前をコロンで囲みます。これで「サブクラス1」から「サブクラス2」のメソッドが呼び出せるようになります。

変数について
続いて値を格納する変数の宣言です。基本的に変数の宣言は以下のように行います。
 
変数の型 変数の名前;
たとえば、文字列を扱うNSStringの変数「word」を作成したい場合は
 
NSString *word;
と記述することで、文字列を扱う「word」インスタンスが生成されます。ここで注意しなければならないのは、インスタンス名の前にアスタリスク「*」が必要となる点です。C言語の変数型、たとえば数字を取り扱う「int」型等の変数にはアスタリスクが必要ありませんが、Objective-Cのために用意されている変数には必ずアスタリスクが必要になります。基本的にObjective-Cで用意されている変数型やクラス名はほとんどNSから始まる(例を出すとNSString、NSObjectなど)ので、NSから始まるクラスのインスタンスを作成する時はインスタンス名の前にアスタリスクが必要になると覚えておきましょう。ちなみに、このアスタリスクを忘れるとプログラムの実行時にエラーメッセージが表示されるので、変数の宣言でエラーメッセージが表示される場合はこのアスタリスクがちゃんと記述できているかを疑ってください。

とは言ったものの、「Hello World」アプリケーションで宣言した変数を見てみると、一般の型宣言と異なっていることがわかります。
 
IBOutlet id text;
これは変数「text」の宣言を行っていますが、その前に「id」と書かれていますね。実は、全ての変数型はこの「id」型で記述することもできます。この「id」型で変数の宣言を行った場合はアスタリスクが必要ありません。先ほど例を出したNSString型の変数「word」を
 
id word;
と記述してしまってもかまわないのです。しかし、変数型を指定せずに全ての変数を「id」型で宣言してしまうと、そのインスタンスがどのような役割を持ているものだったのかわかりにくくなってしまいます。また、誤って違う型の変数を代入してしまった際はエラーメッセージが表示されるので、間違いを見抜きやすくなるといったメリットもあります。特別な場合でない限り、インスタンス変数の宣言では型宣言を行った方が賢明です。「IBOutlet」と書かれた部分に関しては次の「アウトレットとアクション」で詳しく解説します。

アウトレットとアクション
GUIアプリケーションを作成する上でとても重要な概念があります。アウトレットとアクションです。アクションとは、ユーザがボタンを押したときなどに発動する動作の一連のことを言います。アウトレットとはその反対で、テキストフィールドなどに表示する情報のことを指します。「Hello World」アプリケーションでは、ボタンにアクションを接続し、テキストフィールドにアウトレットを接続しました。

「ボタンを押した」という動作を検出した時点で、「AppController」のアクション「message」が呼び出されます。「message」はテキストフィールドに文字列を設定させるよう記述していましたので、アウトレットである「text」に表示したい文字列を渡して、テキストフィールドに「Hello World」と表示させていました。つまり、テキストフィールドはアウトレットとして設定したインスタンス変数である「text」を参照することで、表示させる文字列を得ています。これがInterface Builderで行った割り当て作業です。この一連の作業のことを、アウトレットとアクションの接続と言います。

ただし、インスタンス変数を宣言するだけではInterface Builder上で接続の対象にはなりません。Interface Builderでアウトレットとして認識させるためには、インスタンス変数の宣言の前に「IBOutlet」と記述する必要があります。
 
IBOutlet id text;
このように記述することで、インスタンス変数「text」はInterface Builder上で認識されるようになり、アウトレットとして割り当てができるようになります。

アクション(メソッド)の宣言
ヘッダファイルでは、使用したいアクションの宣言も行います。アクションの正体はメソッドなので、一般のメソッドを記述するのと同じ流儀で記述します。以下の構文がヘッダファイルにあったアクションの宣言です。
 
-(IBAction)message : (id)sender;
一般的に、メソッドの宣言は以下のように構成されています。
 
-(返り値型)メソッド名 : (引数型)引数名;
ここでは深く捉える必要はありません。返り値型にはInterface Builder上でアクションとして認識してもらえるよう、「IBAction」と設定しておき、メソッド名には好きな名前を設定するという事だけ覚えておきましょう。メソッドの突っ込んだ詳しい使い方などは次の章で紹介します。

実装ファイルのコーディング
これでヘッダファイルの解説は終了です。続いて、実装ファイルについて解説します。実装ファイルの始まりを見てみると
 
#import "AppController.h"
@implementation AppController
と記述されています。ヘッダファイルと比較してみると、Cocoaのフレームワークは読み込まず、AppControllerクラスのヘッダファイルをインポートしています。これはなぜでしょうか?

インポートの作業では、インポートしたクラスが何らかのクラスをインポートしている場合、再度インポート文を記述しなくても辿ってインポートを行ってくれるといった特性があります。AppControllerのヘッダファイルではCocoaフレームワークのヘッダファイルをインポートしていたので、AppConrollerさえインポートしておけばCocoaフレームワークの機能を呼び出せるのです。この点はクラスの継承関係によく似ていますね。

メソッドの実装と呼び出し
メソッドの中身を実装するには、まずヘッダファイルで宣言したメソッドをそのまま一行目に記述します。メソッドの実装では、宣言時とは違い一番最後のセミコロン「;」が必要ありません。続けて、括弧の中に「message」メソッドが呼び出されたときの振る舞いを記述します。
 
-(IBAction)message : (id)sender  //メソッド「message」の実装
{
    [text setStringValue:@"Hello World"];
}
括弧の中にもうひとつ違う種類の括弧、[ ]がありますね。Objective-Cでメソッドを呼び出すには、この[ ]の中に使いたいメソッドを記述します。今回は、「text」インスタンスの「setStringValue」というメソッドを呼び出しました。これはテキストフィールドに設定した文字列を表示させるためのメソッドなので、テキストフィールドに表示させたい文字列を引数として渡してあげなければなりません。それが「@"Hello World"」と書かれた部分です。表示させる文字列として、「Hello World」という文字列を設定しています。

構文をまとめると以下のようになります。
 
[インスタンス名 メソッド名:引数];
インスタンス「text」は、ヘッダファイル上でid型として宣言しましたが、Interface Builder上でテキストフィールドと接続したため、正体はNSTextField型のインスタンスです。「NSTextField」クラスの「setStringValue」というメソッドをこの一行で呼び出しています。インスタンス「text」は「NSTextField」クラスから生成されたインスタンスなので、「NSTextField」クラスのメソッドがそのまま使えます。厳密に言えば、コード内で使用した「setStringValue」メソッドは「NSControl」クラスのメソッドです。「NSTextField」クラスは「NSControl」クラスを継承しているので、「NSTextField」のクラスからそのメソッドを呼び出すことができます。ここではそのメソッドを通し、インスタンス「text」が取り扱う変数として「Hello World」という文字列を渡してあげました。

ヘッダファイルと実装ファイルについて
今回は実際にソースコードの中身を解説しましたが、いまいちヘッダファイルと実装ファイルの両方を記述する意味がわからない方がほとんどだと思います。果たして、ヘッダファイルの両方を記述し、ひとつのクラスを作り上げていく意味はあるのでしょうか?

インスタンス変数の宣言は、ヘッダファイル上だけでなく、メソッドの中で宣言することも可能です。「message」メソッドの中に数字を取り扱う「int」型のインスタンス変数、「value」を宣言したとします。
 
-(IBAction)message : (id)sender  //メソッド「message」の実装
{
   int value;
   value = 5;  //「valueは整数の5である」という意味
    [text setStringValue:@"Hello World"];
}
「message」メソッド内でしか使用しないのであればこのままで問題ありませんが、他のメソッドからもこのインスタンス変数「value」を参照、変更したい場合、他のメソッドからはこのインスタンス変数にアクセスする事はできません。なぜなら、この「message」メソッドが実行されない限りプログラム上ではインスタンス変数「value」は存在しないためです。他のメソッドからもインスタンス変数を参照、変更したい場合はヘッダファイルに記述することで、そのクラス内のメソッドであれば自由にインスタンス変数にアクセスすることができます。逆に変更されたくない、あるいは公開したくない場合はヘッダファイルに記述しなくても構わないのです。実装ファイルの冒頭では「AppController」クラスのヘッダファイルをインポートしていました。これは、フレームワークをただインポートするためだけでなく、実はヘッダファイルで宣言したインスタンス変数を参照できるようにするといった役割もあったのです。

メソッドをヘッダファイルに公開するのも同じような意味合いがあります。メソッドの場合、参照元となるのは別のクラスです。別のクラスから使用したいメソッドがある場合には、そのメソッドをヘッダファイルに公開します。今回の「Hello World」アプリケーションの「message」メソッドは、AppControllerクラスでしか使用していませんでした。そのため、ヘッダファイルから削除してしまってもまったく問題ありません。試しに、ヘッダファイルから「message」メソッドの宣言を削除した状態で「ビルドと実行」をしてみましょう。何事もなかったかのように動作したはずです。

このように、Objective-C言語ではヘッダファイルと実装ファイルの二つをコーディングすることでひとつのアプリケーションとして動作することがわかりました。次の章では、インスタンス変数とメソッドについて更に解説したいと思います。

無料でダウンロードできるApple社純正統合開発ツール(IDE)
 
inserted by FC2 system