クラスオブジェクト
クラスはインスタンス化するまで実体がないんだけど,その実体のないクラスもオブジェクトとして考えましょうということ.
変数,メソッドはインスタンス化してから使用可能になるのが通常だけど,インスタンス化しなくても使用可能な変数,メソッドもある(クラス変数,クラスメソッド).
#ちなみにObjective-Cにはクラス変数みたいなものはない.
allocメソッドなんか,まさにクラスメソッド.NSObjectで定義されてる.
クラスオブジェクトの型
オブジェクトはid型で表すことができるが,クラスオブジェクトには特にClass型という型が用意されている.
クラスメソッド
定義は↓こんな感じで.
@interface Class1 : Class0 - (型)インスタンスメソッド + (型)クラスメソッド @end
クラス変数
Objective-Cにはクラス変数という定義のものは存在しないが,static指定子とともに定義された変数をクラス変数の代用として利用可能.
とはいえstaticな変数に直接アクセスすることはできなくて,setter/getterをクラスメソッドにしてアクセスするという使い方になる.
クラスオブジェクトの初期化
initializeメソッドをオーバーライドする.
クラス
クラスはClass型で定義されるが,そのClass型はobjc.hで
typedef struct objc_class *Class;
と定義されている.さらに構造体objc_classはobjc-class.hで,
struct objc_class { struct objc_class* isa; struct objc_class* super_class; const char* name; long version; long info; long instance_size; struct objc_ivar_list* ivars; struct objc_method_list** methodLists; struct objc_cache* cache; struct objc_protocol_list* protocols; };
と定義されている.これがクラスの実態.
- isa
- super_class
- スーパークラスが入ってる.NSObjectのsuper_classにはNULLが入ってる.
- name
- クラスの名前.C文字列.
- version
- クラスのバージョン番号.実行時,class_setVersion関数で変更できる.コンパイラは最初 0 として定義する.
- info
- ランタイム関数によって使われる一連のビットフラグが格納されている.
- CLS_CLASS (0x1L):このクラス定義がクラスを表しており,そのクラスの新しいインスタンスごとに割り当てられるインスタンスメソッドと変数定義が含まれていることを示す.
- CLS_META (0x2L):このクラス定義がメタクラスを表しており,そのクラスのいずれかのインスタンスに固有のものではないメソッドのリスト(クラスメソッド)が含まれていることを示す.
- CLS_INITIALIZED (0x4L):ランタイムによってこのクラスが初期化されたことを示す.このフラグをセットできるのはobjc_addClass関数のみ.
- CLS_POSING (0x8L):このクラスが別のクラスになりすましていることを示す.
- CLS_MAPPED (0x10L):Objective-C ランタイムによって内部的に使用される.
- CLS_FLUSH_CACHE (0x20L):Objective-C ランタイムによって内部的に使用される.
- CLS_GROW_CACHE (0x40L):Objective-C ランタイムによって内部的に使用される.
- CLS_NEED_BIND (0x80L):Objective-C ランタイムによって内部的に使用される.
- CLS_METHOD_ARRAY (0x100L):methodListsフィールドが単一のobjc_method_listデータ構造体へのポインタではなく,objc_method_listデータ構造体のポインタの配列であることを示す.
- ランタイム関数によって使われる一連のビットフラグが格納されている.
- instance_size
- インスタンス変数のサイズを示す.
- ivars
- methodLists
- cache
- objc_cacheメソッドキャッシュデータ構造体へのポインタ。
- protocols
- objc_protocol_listデータ構造体へのポインタ.このクラスが実装しているとされる正式なプロトコルのリスト.
クラスの書き方
クラスは通常インターフェース部と実装部を別々のファイルに記述する.
インターフェースは拡張子.hのインターフェースファイルに記述する.
実装部は拡張子.mの実装ファイルに記述する.
ひとつのファイルに複数のクラスを記述することが可能であるが,通例として1ファイル/1クラスとし,ファイル名は通常,記述されているクラス名と同じにする.
ルートクラスはNSObjectで,あらゆるクラスはNSObjectを根にもつ木構造にぶらさがる感じになっている.
インターフェース
↓こんな感じで書く
@interface Class:SuperClass { var; ・・・ } method ・・・ @end
クラス名は大文字から,変数,メソッド名は小文字からはじめるのが通例.
インターフェースファイルのインポート
↓こんな感じで記述する.
#import "Rectangle.h"
Objective-Cでは#includeの代わりに主に#importを使う.多重呼び出しを防いでくれる.
スーパークラスをインポートすることで,NSObjectからそのスーパークラスまですべての派生クラスの宣言を暗黙のうちに含む.
インターフェイスがその階層以外のクラスを記述している場合は,それらを明示的にインポートするか,@classディレクティブで宣言する必要がある.
インターフェースファイルの役割
- クラスが継承階層にどのように結び付いていて、どのようなクラスが他に必要となるか(継承するか、クラスのどこかで参照するか)をユーザに知らせる.
- オブジェクトに含まれているインスタンス変数をコンパイラに知らせ,サブクラスが継承している変数をプログラマに知らせる.
- メソッド宣言のリストを通して,他のモジュールに,どのようなメッセージをクラスオブジェクトとクラスインスタンスに送信できるかを知らせる.クラス実装の内部で使用するメソッドは省略可.
インスタンス変数は,インターフェイスファイルで宣言する必要がある.
コンパイラがオブジェクトが定義されている場所だけでなく,オブジェクトが使用される場所でもオブジェクトの構造を認識している必要があるから.
クラスの実装
↓こんな感じで書く.
@implement Class:SuperClass { var; } method @end
すべての実装ファイルは,自身のインターフェースファイルをインポートしなければならない.
変数宣言とスーパークラス名は省略可.