Apple Developer Connection
Advanced Search
Member Login Log In | Not a Member? Contact ADC

< Previous PageNext Page >

サブクラスの基本的な設計

適切に設計されたサブクラスはすべて、その祖先や役割に関係なく、いくつかの共通する特徴を持っています。設計が不十分なサブクラスは、エラーを起こしやすく、使用および拡張が難しく、パフォーマンスを低下させます。優れた設計のサブクラスは、これとはまったく逆になります。本稿では、効率性、堅牢性、利便性、そして再利用性を備えたサブクラスを設計するための推奨事項について説明します。

参考資料: 本稿ではサブクラスの作成に関していくつかの手法について触れていますが、説明の中心は基本的な設計に関することがらです。開発アプリケーションであるXcodeおよびInterface Builderを使用してクラス定義の一部を自動化する方法については説明しません。これらのアプリケーションの詳細については、『Xcode Quick Tour Guide』を参照してください。このセクションで説明している、設計に関することがらは、特にモデルオブジェクトに適用できます。『Model Object Implementation Guide』では、モデルオブジェクトの適切な設計と実装について詳しく説明しています。

In this section:

サブクラス定義の形式
スーパークラスメソッドのオーバーライド
インスタンス変数
エントリポイントとエグジットポイント
初期化かデコードか
アクセサメソッド
キー値メカニズム
オブジェクトのインフラストラクチャ
エラー処理
リソース管理その他の効率向上手段
関数、定数、その他のCの型
クラスをパブリックにする場合


サブクラス定義の形式

Objective-Cのクラスは、インターフェイスと実装で構成されています。慣例上、これら2つのクラス定義部分は別々のファイルに分けられます。(ANSI Cヘッダファイルと同様に)インターフェイスファイルは.hという拡張子を持ちます。実装ファイルは.mという拡張子を持ちます。通常、ファイル名のうち拡張子を除いた部分は、クラス名と同じです。たとえば、Controllerという名前のクラスでは、ファイル名は次のようになります。

Controller.h
Controller.m

インターフェイスファイルには、クラスのパブリックインターフェイスを確立する、メソッド(および関数)宣言のリストがあります。また、インスタンス変数、定数、文字列グローバル、その他のデータ型の宣言もあります。@interfaceディレクティブは、インターフェイスに必須の宣言の開始を示し、@endディレクティブは宣言の終了を示します。@interface ディレクティブは、クラスの名前と、直接の継承元クラスの名前を示すので特に重要です。形式は次のとおりです。

@interface ClassName : Superclass

Listing 3-2に、Controllerという架空のクラスのインターフェイスファイルを示します(宣言を加える前の状態)。

Listing 3-2  インターフェイスファイルの基本構造

#import <Foundation/Foundation.h>
 
 
@interface Controller :NSObject {
 
}
 
@end

クラスインターフェイスを完成するには、Figure 3-4に示すように、このインターフェイス構造の適切な場所に、必要な宣言を配置する必要があります。


Figure 3-4  インターフェイスファイルで宣言を配置する場所

Figure 3-4 インターフェイスファイルで宣言を配置する場所

これらの宣言の型の詳細については、“「インスタンス変数」”および“「関数、定数、その他のCの型」”を参照してください。

クラスインターフェイスでは、最初に、適切なCocoaフレームワークと、インターフェイスに出現するすべての型に対応するヘッダファイルをインポートします。#import プリプロセッサコマンドは、指定したヘッダファイルを取り込むという点では#includeに似ています。しかし、#importでは、コンパイル時に、以前に直接または間接にインクルードしていない場合にのみファイルをインクルードすることで効率を向上させています。コマンドの後に続く<...>によって、ヘッダファイルに対応するフレームワークを示し、スラッシュ文字の後がヘッダファイルを示します。#import行の構文は次のような形式になります。

#import < Framework / File .h>

Frameworkは、システムにおいてフレームワークのための標準の場所のいずれかになければなりません。Listing 3-2の例では、クラスのインターフェイスファイルの中で、FoundationフレームワークのFoundation.hファイルをインポートしています。この例のように、Cocoaの規則では、フレームワークと同じ名前を持つヘッダファイルには、フレームワークのすべてのパブリックインターフェイス(およびその他のパブリックヘッダ)をインクルードする一連の#importコマンドが含まれています。ちなみに、定義しようとしているクラスがアプリケーションの一部である場合は、CocoaアンブレラフレームワークのCocoa.hファイルをインポートするだけで済みます。

クラスの実装ファイルの構造は、Listing 3-3に示すようにもっと単純です。クラスの実装ファイルでは、最初にクラスのインターフェイスファイルをインポートすることが重要です。

Listing 3-3  実装ファイルの基本構造

#import "Controller.h"
 
 
@implementation Controller
 
 
@end

メソッド実装はすべて、@implementationディレクティブから@endディレクティブまでの間に記述する必要があります。クラスに関係する関数の実装は、ファイルの任意の場所に記述できますが、慣例上、それらも2つのディレクティブの間に記述します。プライベート型の宣言(関数や構造体など)は、通常は#importコマンドと@implementationディレクティブの間に記述します。

スーパークラスメソッドのオーバーライド

カスタムクラスはその定義上、何らかのプログラム固有の方法によって、そのスーパークラスの動作を変更します。スーパークラスのメソッドをオーバーライドすることが、通常はこのような変更を達成する手段になります。サブクラスを設計する際には、オーバーライドするメソッドを特定し、それらをどのように実装しなおすかを考えるという手順が必要になります。

いくつかの一般的なガイドラインについては“「メソッドのオーバーライドを必要とする場合」”で説明していますが、どのスーパークラスメソッドをオーバーライドするかを特定するためには、クラスを詳しく調べる必要があります。クラスのヘッダファイルとドキュメントをよく読みます。また、スーパークラスの指定イニシャライザを探します。初期化を正常に行うには、クラスの指定イニシャライザでスーパークラスの指定イニシャライザを呼び出す必要があるためです(詳細については、“「オブジェクトの作成」”を参照してください)。

メソッドを特定したら、まず、(これは純粋に実践における手法なのですが)オーバーライドするメソッドの宣言を自分のインターフェイスファイルにコピーします。次に、同じ宣言を自分の.mファイルにコピーし、終端のセミコロンを閉じ中括弧に置き換えて、メソッドのスケルトン実装とします。

“「メソッドのオーバーライドを必要とする場合」”で説明したように、オーバーライドしたフレームワークメソッドは通常は(自分のコードからではなく)Cocoaフレームワークから呼び出されます。場合によっては、元のメソッドではなくオーバーライド版のメソッドを呼び出す必要があることを、フレームワークに知らせる必要があります。Cocoaではこのために各種の手段が用意されています。たとえば、Interface Builderアプリケーションでは、「Inspector」(情報)ウインドウで、(互換性のある)フレームワーククラスを自分のクラスに置き換えることができます。カスタムのNSCellクラスを作成した場合は、NSControl setCell:メソッドを使用して、特定のコントロールをクラスに関連付けることができます。

インスタンス変数

カスタムクラスを作成する理由は、スーパークラスの動作を変更するためだけでなく、プロパティを追加するためでもあります。ここでの“プロパティ”とは、サブクラスのインスタンスの属性と、インスタンスが持つほかのオブジェクトへの参照(つまり、関係)の両方を意味します。たとえば、Circleという架空のクラスがあったとします。図形に色を追加するサブクラスを作成する場合、そのサブクラスは何らかの方法で色の属性を持つ必要があります。色の属性を持つために、colorというインスタンス変数(おそらくNSColorオブジェクト型)をクラスインターフェイスに追加することが考えられます。カスタムクラスのインスタンスでは、この新しい属性がカプセル化され、特徴を示す永続的なデータとして保持されます。

Objective-Cのインスタンス変数は、クラス定義の一部であるオブジェクトや構造体その他のデータ型を宣言したものです。これらがオブジェクトの場合、オブジェクトは動的な型として宣言されるか(idを使用)、静的な型として宣言されるかのどちらかになります。両方の形式を次の例に示します。

id delegate;
NSColor *color;

一般に、オブジェクトのクラスのメンバが不定であるか、重要ではない場合は、オブジェクトインスタンス変数を動的な型として宣言します。インスタンス変数として保持されるオブジェクトは、親オブジェクトによってすでに保持されている場合(委任の場合と同様)を除いて、作成するか、コピーするか、または明示的に保持します。インスタンスを展開する場合は、オブジェクトインスタンス変数を initWithCoder:の中でデコードして割り当てを行っている間、インスタンス変数を保持しておく必要があります(オブジェクトのアーカイブと展開の詳細については、“「エントリポイントとエグジットポイント」”を参照してください)。

命名規則として、インスタンス変数の名前は句読点や特殊文字を含まない小文字の文字列にします。名前に複数の単語を含める場合は、それらをそのまま続けて記述しますが、2番目以降の各単語の頭文字を大文字にします。たとえば、次のようになります。

NSString *title;
NSColor *backgroundColor;
NSRange currentSelectedRange;

インスタンス変数の宣言の前にIBOutletタグを付けた場合は、接続がnibファイルにアーカイブされている(と考えられる)アウトレットを示します。また、このタグによって、Interface BuilderとXcodeの動作の連携が可能になります。Interface Builderのnibファイルから展開されるアウトレットは、自動的に保持されます。

インスタンス変数は、オブジェクトのクライアントに供給できる、オブジェクトの属性以外のものも持つことができます。オブジェクトによって実行される何らかの処理の基礎として使用されるプライベートデータを、インスタンス変数に保持できる場合もあります。バッキングストアやキャッシュなどがその例です(データがインスタンスごとに固有ではなく、クラスのインスタンスどうしで共有する場合は、インスタンス変数ではなくグローバル変数を使用します)。

インスタンス変数をサブクラスに追加する際には、次に示すいくつかのガイドラインに留意します。

エントリポイントとエグジットポイント

オブジェクトには、その存続期間の間、Cocoaフレームワークからさまざまな時点でオブジェクトメッセージが送信されます。ほとんどすべてのオブジェクト(クラスを含ます。クラス自体も実際にはオブジェクトです)は、実行時の存続期間の最初と、破棄の直前に、特定のメッセージを受信します。これらのメッセージによって呼び出されるメソッド(実装されている場合)は、その時点の関連する作業をオブジェクトが実行できるようにする“フック”となります。以下では、これらのメソッドを呼び出される順に示します。

  1. initializeクラスではこのクラスメソッドを使用して、クラスまたはクラスのインスタンスがほかのメッセージを受信する前に、自身を初期化できます。スーパークラスは、サブクラスよりも先にこのメッセージを受信します。古いアーカイブメカニズムを使用しているクラスでは、initializeを実装して自身のクラスバージョンを設定できます。そのほか、initializeは、何らかのサービスにクラスを登録する場合や、すべてのインスタンスで使用される何らかのグローバルな状態を初期化する場合にも使用します。ただし、この種の処理はinitializeで実行するよりも、インスタンスメソッドで、遅らせて(つまり、必要になった時点で初めて)実行したほうがよい場合もあります。

  2. init (または他のイニシャライザ)―スーパークラスの指定イニシャライザによる処理がクラスにとって十分な場合を除き、インスタンスの状態を初期化するためにinitまたはその他の何らかのイニシャライザを実装する必要があります。指定イニシャライザなどのイニシャライザの詳細については、“「オブジェクトの作成」”を参照してください。

  3. initWithCoder:たとえば、自分のクラスがモデルクラスの場合など、自分のクラスのオブジェクトがアーカイブされることを前提とする場合は、NSCodingプロトコルを採用し(必要な場合)、その2つのメソッド(initWithCoder:および encodeWithCoder:)を実装します。. これらのメソッドではそれぞれ、オブジェクトのインスタンス変数をアーカイブに適した形式でデコードおよびエンコードする必要があります。この目的のために、NSCoderのデコード用およびエンコード用のメソッドを使用したり、キーアーカイブ機能(NSKeyedArchiverおよびNSKeyedUnarchiver)を使用したりできます。オブジェクトが明示的に作成されるのではなく展開されようとしているときは、イニシャライザではなくinitWithCoder:メソッドが呼び出されます。このメソッドでは、インスタンス変数の値をデコードした後に、それらの値を該当する変数に割り当て、必要に応じて保持またはコピーします。この話題の詳細については、『Archives and Serializations Programming Guide for Cocoa』を参照してください。

  4. awakeFromNibアプリケーションでnibファイルをロードすると、アーカイブからロードされた各オブジェクトにawakeFromNibメッセージが送信されますが、送信されるのは対象オブジェクトがメッセージに応答できる場合で、なおかつ、アーカイブ内のすべてのオブジェクトのロードと初期化が完了した後のみです。awakeFromNibメッセージを受信したオブジェクトは、そのすべてのアウトレットインスタンス変数が設定されることが保証されます。通常、nibファイルを所有しているオブジェクト(ファイルの所有者)がawakeFromNibを実装し、アウトレットとターゲット/アクションの接続の設定を必要とする初期化処理を実行します。

  5. encodeWithCoder:―このメソッドは、クラスのインスタンスをアーカイブする必要がある場合に実装します。このメソッドは、オブジェクトの破棄の直前に呼び出されます。詳細については、前述のinitWithCoder:の説明を参照してください。

  6. deallocこのメソッドは、インスタンス変数を解放し、自分のクラスのインスタンスで確保したほかのメモリの割り当てをすべて解除するために実装します。このメソッドが制御を戻した直後に、インスタンスが破棄されます。deallocメソッドの詳細については、“「オブジェクトの作成」”を参照してください。

アプリケーションのグローバルアプリケーションオブジェクト(NSAppとして識別されます)もまた、オブジェクトがNSAppのデリゲートで、適切なメソッドを実装している場合に、アプリケーションの存続期間の最初と最後に、オブジェクトにメッセージを送信します。アプリケーションの起動の直後に、NSAppはメッセージとしてapplicationWillFinishLaunching:およびapplicationDidFinishLaunching:を自身のデリゲートに送信します(前者は、ダブルクリックされたドキュメントが開く前に、後者はドキュメントが開いた直後に、それぞれ送信されます)。どちらのメソッドにおいても、アプリケーションの実行時の存続期間における早い段階で1回だけ実行する必要のある、グローバルなアプリケーションロジックをデリゲートの中で指定できます。アプリケーションの終了の直前に、NSAppapplicationWillTerminate:を自身のデリゲートに送信します。デリゲートでは、ドキュメントやアプリケーションの状態を保存することによって、プログラムの終了を適切に処理するためのメソッドを実装できます。

このほか、Cocoaフレームワークには、ほかにウインドウのクローズからアプリケーションのアクティブ化および非アクティブ化にいたるさまざまなイベントに対応したフックが多数用意されています。通常、これらは委任メッセージとして実装します。また、オブジェクトをフレームワークオブジェクトのデリゲートにして、必要なメソッドを実装する必要があります。また、フックが通知である場合もあります(委任の詳細については、“「オブジェクトとの通信」”を参照してください。

初期化かデコードか

作成するクラスのオブジェクトをアーカイブおよび展開させる場合、クラスはNSCodingプロトコルに準拠する必要があります。オブジェクトをエンコードするメソッド(encodeWithCoder:)とオブジェクトをデコードするメソッドinitWithCoder:)をクラスに実装する必要があります。アーカイブから作成されるオブジェクトの初期化には、1つまたは複数のイニシャライザメソッドではなく、initWithCoder:メソッドが呼び出されます。

クラスのイニシャライザメソッドとinitWithCoder:はほとんど同じ処理を実行する可能性があるため、共通の処理を、イニシャライザとinitWithCoder:の両方から呼び出されるヘルパーメソッドに集めるのは意味があります。たとえば、セットアップルーチンの一部として、オブジェクトでドラッグの種類とドラッグ元を指定する場合、オブジェクトにListing 3-4に示すようなヘルパーメソッドを実装することが考えられます。

Listing 3-4  初期化ヘルパーメソッド

 (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self setTitleColor:[NSColor lightGrayColor]];
        [self registerForDragging];
    }
    return self;
}
- (id)initWithCoder:(NSCoder *)aCoder {
    self = [super initWithCoder:aCoder];
    titleColor = [[aCoder decodeObject] copy];
    [self registerForDragging];
    return self;
}
 
- (void)registerForDragging {
    [theView registerForDraggedTypes:
        [NSArray arrayWithObjects:DragDropSimplePboardType, NSStringPboardType,
        NSFilenamesPboardType, nil]];     [theView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES]; }

クラスのイニシャライザとinitWithCoder:はこのように同等の役割を果たせますが、例外もあります。フレームワーククラスのカスタムサブクラスを作成し、そのインスタンスがInterface Builderパレットに出現する場合です。通常、そのような場合は、プロジェクトでクラスを定義し、オブジェクトをInterface Builderパレットからインターフェイスにドラッグした後、Interface Builderの情報ウインドウの「Custom Class」ペインを使用して、そのオブジェクトをカスタムサブクラスに関連付けます。ただし、この場合、サブクラスのinitWithCoder:メソッドが展開中に呼び出されることはなく、代わりにinitメッセージが送信されます。カスタムオブジェクトの特別な設定処理は awakeFromNibの中で実行してください。このメソッドは、nibファイルのすべてのオブジェクトの展開が完了した後に呼び出されます。

アクセサメソッド

アクセサメソッド(または単に“アクセサ”)は、インスタンス変数の値を取得または設定するもので、適切に設計されたクラスのインスタンスにおいて必要不可欠な要素です。アクセサメソッドはオブジェクトのプロパティへの門のような機能を果たし、プロパティへのアクセスを制限することでオブジェクトのインスタンスデータのカプセル化を実現します。

規則上の理由から、また、クラスをキー値コーディング(“「キー値メカニズム」”を参照)に準拠させるために、アクセサメソッドには特定の形式の名前を付ける必要があります。インスタンス変数の値を返すメソッド(しばしばgetterと呼ばれます)の名前は、単純にインスタンス変数の名前です。インスタンス変数の値を設定するメソッド(setter)の名前は、“set”で始まり、その直後にインスタンス変数の名前が付きます(1文字目を大文字にします)。たとえば、“color”という名前のインスタンス変数の場合、getterおよびsetterアクセサの宣言は次のようになります。

- (NSColor *)color;
- (void)setColor:(NSColor *)aColor;

インスタンス変数がintfloatなどのCのスカラー型の場合、アクセサメソッドの実装は非常に単純になる傾向があります。たとえば、名前がcurrentRateで型がfloatのインスタンス変数の場合、アクセサメソッドの実装方法はListing 3-5に示すようなものになります。

Listing 3-5  非オブジェクトインスタンス変数の場合のアクセサの実装

- (float)currentRate {
    return currentRate;
}
 
- (void)setCurrentRate:(float)newRate {
    currentRate = newRate;
}

インスタンス変数にオブジェクトを保持する場合は、状況がもう少し複雑になります。これらのオブジェクトはインスタンス変数であるため、永続的なものにする必要があります。したがって、割り当て時に作成、コピー、または保持する必要があります。setterアクセサでインスタンス変数の値を変更した場合、setterアクセサでは永続性を確保するだけでなく、古い値を適切に破棄する必要もあります。getterアクセサは、インスタンス変数の値を要求側のオブジェクトに供給します。オブジェクトの所有権に関するCocoaのポリシーから導かれる次の2つの前提に立つと、どちらの種類のアクセサの操作も、メモリ管理に影響を及ぼします。

この2つの前提を念頭に置きつつ、titleという名前のNSStringインスタンス変数について、そのgetterおよびsetterアクセサの実装例を2つ考えてみましょう。まず、Listing 3-6を示します。

Listing 3-6  オブジェクトインスタンス変数用のアクセサの実装—良い例

- (NSString *)title {
    return title;
 }
 - (void)setTitle:(NSString *)newTitle {
    if (title != newTitle) {
        [title autorelease];
        title = [newTitle copy];
    }
 }

getterアクセサは、単純にインスタンス変数への参照を返すだけです。一方、setterアクセサはgetterアクセサよりも処理が多くなります。インスタンス変数に対して渡された値が現在の値と同じでないことを確かめた後、現在の値を自動解放してから、新しい値をインスタンス変数にコピーします(autoreleaseをオブジェクトに送信するほうが、releaseを送信するよりも“スレッドセーフ”になります)。ただし、この手法では依然として潜在的な危険が伴います。getterアクセサから返されたオブジェクトをクライアントで使用している間に、setterアクセサが古いNSStringオブジェクトを自動解放し、その直後にオブジェクトが解放され破棄された場合はどうなるでしょう。その場合、クライアントオブジェクトが持つインスタンス変数への参照が有効でなくなります。

Listing 3-7に示すもう1つのアクセサメソッドの実装例では、getterメソッドの中でインスタンス変数の値を保持し、それから自動解放することによって、この問題を回避しています。

Listing 3-7  オブジェクトインスタンス変数用のアクセサの実装—より良い例

 - (NSString *)title {
    return [[title retain] autorelease];
 }
 - (void)setTitle:(NSString *)newTitle {
    if (title != newTitle) {
        [title release];
        title = [newTitle copy];
    }
}

どちらの例(Listing 3-6およびListing 3-7)でも、setterメソッドで新しいNSStringインスタンス変数を保持するのではなく コピーしています。なぜ保持しないのでしょう。一般的な規則として、インスタンス変数に代入されたオブジェクトが値オブジェクトの場合、つまり、文字列や日付、数値、企業レコードなどの何らかの属性を表すオブジェクトの場合は、コピーします。属性値を保持することに関心があり、自分の知らないところで変異させられる危険は避けることが目的です。つまり、オブジェクトの独自のコピーを持つのです。

一方、保存およびアクセスの対象となるオブジェクトが、NSViewNSWindowオブジェクトなどのエンティティオブジェクトの場合、それを保持する必要があります。エンティティオブジェクトはより集約的でリレーショナルなため、それらをコピーすると負担が大きくなる可能性があるからです。オブジェクトが値オブジェクトかエンティティオブジェクトかを決めるには、オブジェクトの値に注目しているのか、それともオブジェクトそのものに注目しているのかを判断するのが1つの方法です。注目の対象が値の場合は、おそらく値オブジェクトであり、コピーしてください(もちろん、オブジェクトがNSCopyingプロトコルに準拠していることが前提です)。

setterメソッドでインスタンス変数を保持するかコピーするかを決める別の方法として、インスタンス変数が属性なのか関係なのかを調べるという方法があります。この方法は、アプリケーションのデータを表すオブジェクトであるモデルオブジェクトの場合に特に有効です。属性とは、本質的には値オブジェクトと同じであり、たとえば色(NSColorオブジェクト)やタイトル(NSStringオブジェクト)など、属性をカプセル化しているオブジェクトを定義する特性としてのオブジェクトです。一方、関係とは、1つまたは複数のほかのオブジェクト(またはそれらのオブジェクトへの参照)との間の関係のことです。通常、setterメソッドでは、属性の値はコピーし、関係は保持します。ただし、関係には濃度の概念があり、1対1または1対多になる可能性があります。1対多の関係は、通常はNSArrayNSSetのインスタンスなどのコレクションオブジェクトによって表現されますが、その場合、単にインスタンス変数を保持すること以外に何らかの処理をsetterメソッドで実行しなければならないことがあります。詳細については、『Model Object Implementation Guide』を参照してください。属性または関係としてのオブジェクトプロパティの詳細については、“「キー値メカニズム」”を参照してください。

クラスのsetterアクセサをListing 3-6またはListing 3-7に示すように実装した場合、クラスのdeallocメソッドでインスタンス変数の割り当てを解除するには、引数にnilを指定して適切なsetterメソッドを呼び出すだけで済みます。

キー値メカニズム

キー値バインディング、キー値コーディング、キー値監視など、名称に“キー値”を含むいくつかのメカニズムはCocoaの基礎的な構成要素です。これらは、たとえばバインディング(オブジェクト間で値のやり取りや値の同期を自動的に行う技術)など、Cocoaの技術に欠かせない要素です。また、これは、アプリケーションをスクリプト対応にする(つまり、AppleScriptのコマンドに対応できるようにする)ためのインフラストラクチャの少なくとも一部を提供します。特に、キー値コーディングとキー値監視は、カスタムのサブクラスを設計するうえで重要な考慮事項です。

“キー値”という言葉は、プロパティの名前を、その値を取得するためのキーとして使用する技術を指します。この言葉はオブジェクトモデリングパターンの用語の一部です。オブジェクトモデリングパターンは、リレーショナルデータベースの記述に使用されるエンティティ-関係モデリングから派生しています。オブジェクトモデリングでは、オブジェクト(特にModel-View-Controllerパターンの、データを持つモデルオブジェクト)がプロパティを持ちますが、それらのプロパティは通常は(必ずではありませんが)インスタンス変数の形式をとります。プロパティは、名前や色などの属性か、あるいは1つまたは複数のほかのオブジェクトへの参照です。これらの参照のことを関係と呼び、関係は1対1または1対多になる可能性があります。プログラム内のオブジェクトのネットワークは、オブジェクトどうしの相互の関係を通じてオブジェクトグラフを形成します。オブジェクトモデリングでは、キーパス(ドットで区切られたキーからなる文字列)を使用して、オブジェクトグラフの中で関係をたどり、オブジェクトのプロパティにアクセスすることができます。

注: オブジェクトモデリングの詳細については、“「オブジェクトモデリング」”を参照してください。

キー値バインディング、キー値コーディング、およびキー値監視は、このトラバーサルを可能にするメカニズムです。

サブクラスの各プロパティをキー値コーディングの要件に準拠させるには、次を実行します。

自動オブザーバ通知機能で十分な場合は、オブジェクトがKVC準拠であることを確認するだけで、オブジェクトがKVO準拠になります。ただし、キー値監視を手動で実装することもできます。その場合はさらに作業が必要になります。

参考資料: これらの技術の詳細については、『Key-Value Coding Programming Guide』および『Key-Value Observing Programming Guide』を参照してください。KVC、KVO、KVBを使用したCocoaバインディング技術の詳細については、“「オブジェクトとの通信」”“「バインディング」”を参照してください。

オブジェクトのインフラストラクチャ

サブクラスの設計が適切であれば、そのクラスのインスタンスはCocoaオブジェクトに求められているとおりに動作します。そのオブジェクトを使用するコードでは、オブジェクトをクラスのほかのインスタンスと比較したり、オブジェクトの内容を(デバッガなどで)検出したりするなど、オブジェクトを対象に同様の基本的な操作を実行できます。

カスタムのサブクラスでは、次に示すルートクラスメソッドと基本プロトコルメソッドの大部分(必ずしもすべてではありませんが)を実装することが求められます。

作成するクラスのいずれかの祖先が正式プロトコルを採用している場合は、自分のクラスもそのプロトコルに正しく準拠していることを保証する必要があります。つまり、スーパークラスにおけるいずれかのプロトコルメソッドの実装が自分のクラスにとって適切でない場合は、それらのメソッドを自分のクラスで実装しなおします。

エラー処理

プログラマがエラーを適切に処理しなければならないことは、どのようなプログラミングの原則においても自明なことです。しかし、多くの場合、何が“適切”かは、プログラミング言語やアプリケーション環境その他の要因によって異なります。Cocoaでは、サブクラスのコードでエラーを処理する場合の一連の規則と対処法が独自に設けられています。

NSErrorオブジェクト、エラー処理、およびエラー警告の表示の詳細については、『Error Handling Programming Guide For Cocoa』を参照してください。

リソース管理その他の効率向上手段

オブジェクトのパフォーマンスはさまざまな手段を講じて向上できます。もちろん、これらのオブジェクトを組み込んで管理するアプリケーションについても、同じ手段を通じてパフォーマンスを向上できます。そのための手順や原則は、マルチスレッド処理から、描画の最適化、コードのメモリ使用量を減らすための手法など、多岐にわたります。これらの手法の詳細については、『Cocoa Performance Guidelines』や、パフォーマンスに関するその他の文書を参照してください。

しかし、次に示す3つの単純かつ常識的なガイドラインに従うことによって、より高度な手法を採用しなくても、オブジェクトのパフォーマンスを大幅に向上させることができます。

関数、定数、その他のCの型

Objective-CはANSI Cのスーパーセットであるため、関数、typedef構造体、enum定数、マクロといった、任意のCの型をコードで使用できます。設計上の重要な問題として、カスタムクラスのインターフェイスと実装の中で、どのような場合に、どのような方法で、これらの型を使用するのか、ということがあります。

カスタムクラスの定義におけるCの型の使用に関して、いくつかのガイドラインを次に示します。

クラスをパブリックにする場合

アプリケーションのコントローラクラスなど、自分で使用するカスタムクラスを定義する場合は、クラスについて熟知しており、必要な場合にいつでも設計しなおせるので、高い柔軟性が得られます。しかし、カスタムクラスがほかの開発者のスーパークラス、つまり、パブリッククラスになる可能性が高い場合には、自分のクラスに対する他の開発者の想定を考慮して、より慎重な設計を行う必要があります。

Cocoaのパブリッククラスを開発する際のガイドラインをいくつか示します。



< Previous PageNext Page >


Last updated: 2006-05-23




Did this document help you?
Yes: Tell us what works for you.

It’s good, but: Report typos, inaccuracies, and so forth.

It wasn’t helpful: Tell us what would have helped.
Get information on Apple products.
Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Copyright © 2007 Apple Inc.
All rights reserved. | Terms of use | Privacy Notice