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

< Previous PageNext Page >

イントロスペクション

イントロスペクションは、オブジェクト指向の言語および環境が持つ強力な機能であり、Objective-CおよびCocoaはこの機能に対して特に柔軟性があります。イントロスペクションとは、オブジェクトが自身に関する詳細をオブジェクトとして実行時に公開する機能のことです。そのような詳細には、継承ツリーにおけるオブジェクトの位置や、オブジェクトが特定のプロトコルに準拠しているかどうか、オブジェクトが特定のメッセージに応答するかどうかなどがあります。NSObjectプロトコルおよびクラスでは、イントロスペクションメソッドが多数定義されており、それらを使用してランタイムに問い合わせを行ってオブジェクトの性質を調べることができます。

イントロスペクションを的確に使用すれば、オブジェクト指向プログラムの効率や堅牢性が高まります。メッセージのディスパッチエラーや、オブジェクトの等価性に関する誤った仮定、および同類の問題を避けるのに役立てることができます。以降のセクションでは、コードの中でNSObjectのイントロスペクションメソッドを効果的に使用する方法について説明します。

In this section:

継承関係の評価
メソッドの実装とプロトコルへの準拠
オブジェクトの比較


継承関係の評価

オブジェクトが属しているクラスがわかれば、オブジェクトについてもかなりのことがわかります。オブジェクトがどのような機能を持ち、どのような属性を表し、どのような種類のメッセージに応答できるかがわかる可能性があります。イントロスペクションを行い、それでもオブジェクトが属するクラスのことがよくがわからなかったとしても、特定のメッセージをそのオブジェクトに送るべきではないことは十分にわかります。

NSObjectプロトコルでは、クラス階層におけるオブジェクトの位置を調べるためのメソッドがいくつか宣言されています。これらのメソッドは異なる細かさで動作します。たとえば、classおよびsuperclassインスタンスメソッドは、それぞれレシーバのクラスおよびスーパークラスを表すClassオブジェクトを返します。これらのメソッドではClassオブジェクトどうしを比較する必要があります。ごく簡単な使用例をListing 2-7に示します。

Listing 2-7  classメソッドとsuperclassメソッドの使用

// ...
while ( id anObject = [objectEnumerator nextObject] ) {
    if ( [self class] == [anObject superclass] ) {
        // 適切な処理を実行
    }
}

注: 場合によっては、classまたはsuperclassメソッドを使用してクラスメッセージに対する適切なレシーバを取得することもありします。

よくあるのは、オブジェクトのクラスの所属を調べるために、isKindOfClass:またはisMemberOfClass:メッセージをオブジェクトに送ります。前者のメソッドは、レシーバが、指定したクラスのインスタンスか、またはそのクラスを継承するクラスのインスタンスかを示します。一方、isMemberOfClass:メッセージは、レシーバが、指定したクラスのインスタンスかどうかを示します。通常は、isKindOfClass:メソッドのほうが、オブジェクトに送れるすべてのメッセージを一度に知ることができるので便利です。次のListing 2-8のコードを見てみましょう。

Listing 2-8  isKindOfClass:の使用

if ([item isKindOfClass:[NSData class]]) {
    const unsigned char *bytes = [item bytes];
    unsigned int length = [item length];
    // ...
}

このコードでは、オブジェクトのitemNSDataクラスを継承している知ることで、NSData bytesメッセージとlengthメッセージをオブジェクトに送信できることがわかります。isKindOfClass:isMemberOfClass:の違いは、itemがNSMutableDataのインスタンスであると想定すれば明らかです。isKindOfClass:ではなくisMemberOfClass:を使用した場合は、itemNSDataのインスタンスではなく、NSDataのサブクラスであるNSMutableDataのインスタンスであるため、条件ブロック内のコードは決して実行されません。

メソッドの実装とプロトコルへの準拠

NSObjectが備えている、より強力な2つのイントロスペクションメソッドが、respondsToSelector:conformsToProtocol:です。前者は、オブジェクトに特定のメソッドが実装されているかどうかを示します。後者は、指定した正式プロトコルにオブジェクトが準拠しているかどうか(つまり、必要に応じてプロトコルを採用し、プロトコルのすべてのメソッドを実装しているかどうか)を示します。

これらのメソッドはコードの中で似たような状況で使用します。これらを使用すると、特定のメッセージまたはメッセージ群のうちいずれかのメッセージを送信する前に、その対象となる、匿名の場合もある何らかのオブジェクトがそのメッセージに適切に応答するかどうかを調べることができます。メッセージの送信前にこの確認を行うことで、認識されないセレクタに起因する実行時例外が発生する危険を避けられます。Application Kitでは、委任の基礎となる簡易プロトコルが実装されています。これらは、デリゲートに委任メソッドが実装されているかどうかを、そのメソッドを呼び出す前に(respondsToSelector:を使用して)チェックします。

Listing 2-9は、コードにおけるrespondsToSelector:メソッドの使い方を示します。

Listing 2-9  respondsToSelector:の使用

- (void)doCommandBySelector:(SEL)aSelector {
    if ([self respondsToSelector:aSelector]) {
        [self performSelector:aSelector withObject:nil];
    } else {
        [_client doCommandBySelector:aSelector];
    }
}

Listing 2-10は、コードにおけるconformsToProtocol:メソッドの使い方を示します。

Listing 2-10  conformsToProtocol:の使用

// ...
if (!([((id)testObject) conformsToProtocol:@protocol(NSMenuItem)])) {
    NSLog(@"Custom MenuItem, '%@', not loaded; it must conform to the
        'NSMenuItem' protocol.\n", [testObject class]);
    [testObject release];
    testObject = nil;
}

オブジェクトの比較

hashメソッドとisEqual:メソッドは、厳密にはイントロスペクションメソッドではありませんが、同様の役割を果たします。これらはオブジェクトの識別と比較を行うための必要不可欠なランタイムツールです。ただし、これらのメソッドはオブジェクトに関する情報をランタイムに問い合わせるのではなく、クラス固有の比較ロジックを利用します。

hashメソッドとisEqual:メソッドはどちらもNSObjectプロトコルによって宣言されており、密接に関連しています。hashメソッドは、ハッシュテーブル構造のテーブルアドレスとして使用できる整数を返すように実装する必要があります。2つのオブジェクトが(isEqual:メソッドの判定によって)等しい場合、それらは必ず同じハッシュ値を持ちます。オブジェクトをNSSetなどのコレクションに含める可能性がある場合は、hashを定義し、2つのオブジェクトが等しい場合に同じハッシュ値を返すという不変条件を確かめる必要があります。NSObjectにおけるisEqual:のデフォルトの実装は、単純にポインタの等価性を確認するだけです。

isEqual:メソッドの使いかたは単純で、レシーバと、引数として渡されたオブジェクトとを比較します。オブジェクトの比較は、多くの場合、オブジェクトを処理する方法についての情報を実行時の判断部に提供します。Listing 2-11に示すように、isEqual:を使用して、特定の処理を行うべきかどうか(この例では、変更されたユーザ設定を保存するかどうか)を決めることができます。

Listing 2-11  isEqual:の使用

- (void)saveDefaults {
    NSDictionary *prefs = [self preferences];
    if (![origValues isEqual:prefs]) 
        [Preferences savePreferencesToDefaults:prefs];
}

サブクラスを作成する場合は、等価性の確認するためのポイントをさらに追加するためにisEqual:をオーバーライドしなければならないことがあります。サブクラスにおいて、2つのインスタンスが等価であると見なされるために同じ値でなければならない属性を定義することが考えられます。たとえば、MyWidgetという、NSObjectのサブクラスを作成するとします。MyWidgetにはnameおよびdataという2つのインスタンス変数があります。.MyWidgetの2つのインスタンスが等価であると見なされるためには、両方のインスタンス変数が同じ値である必要があります。Listing 2-12は、MyWidgetクラスにおけるisEqual:の実装例を示します。

Listing 2-12  isEqual:のオーバーライド

- (BOOL)isEqual:(id)other {
    if (other == self) 
        return YES;
    if (!other || ![other isKindOfClass:[self class]]) 
        return NO;
    return [self isEqualToWidget:other];
}
 
- (BOOL)isEqualToWidget:(MyWidget *)aWidget {
    if (self == aWidget) 
        return YES;
    if (![(id)[self name] isEqual:[aWidget name]])
        return NO;
    if (![[self data] isEqualToData:[aWidget data]])
        return NO;
    return YES;
}

このisEqual:メソッドは、最初にポインタの等価性を確認し、次にクラスの等価性を確認して、最後にオブジェクトの比較処理を呼び出します。比較処理の名前は、比較の対象となるオブジェクトのクラスを示します。この種の比較処理はCocoaの共通規則であり、渡されたオブジェクトの型チェックを強制的に実施します。NSStringisEqualToString:NSTimeZoneisEqualToTimeZone:はほんの2つの例にすぎません。クラス固有の比較処理(この例ではisEqualToWidget:)では、名前とデータの等価性が確認されます。

CocoaフレームワークのすべてのisEqualTo Type :メソッドにおいて、nilは有効な引数ではなく、それらのメソッドの実装はnilを受け取った時点で直ちに例外を引き起こす可能性があります。ただし、下位互換性を保つため、CocoaフレームワークのisEqual:メソッドはnilを受け入れ、NOを返します。



< 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