コードをもっとオブジェクティブにCocoaの素、Objective-Cを知ろう(6)(3/4 ページ)

» 2009年03月06日 00時00分 公開
[竹下肯己株式会社 qnote]

オブジェクトの判定

 Objective-Cでは、id型であらゆるオブジェクトを表現できます。また、前回紹介したNSArrayなどの配列は、あらゆる型のオブジェクトを要素として保持することができます。

 これは柔軟なプログラミングを可能にする非常に便利な仕組みですが、あるオブジェクトが実際にはどういったクラスのインスタンスであるのかという、判定が必要になる場面もあります。

 以下に、オブジェクトの情報を確認する方法を見てみましょう。

#import <Foundation/Foundation.h>
#import <stdio.h>

@protocol ProtocolA
- (void) testMethod1;
@end

@interface MyBaseCls : NSObject
@end
@implementation MyBaseCls
@end

@interface TestCls : MyBaseCls <ProtocolA>
@end
@implementation TestCls
- (void) testMethod1 {}
- (void) testMethod2:(int)arg1 arg2:(int)arg2 { printf("testMethod2\n"); }
@end


int main(void) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    id obj = [[TestCls alloc] init];

    //(1)あるクラスのオブジェクトか
    BOOL bool1 = [obj isMemberOfClass:[TestCls class]];

    //(2)あるクラスのサブクラスか
    BOOL bool2 = [obj isKindOfClass:[MyBaseCls class]];

    //(3)あるプロトコルを採用しているか
    BOOL bool3 = [obj conformsToProtocol:@protocol(ProtocolA)];

    //(4)あるメソッドを実装しているか
    BOOL bool4 = [obj respondsToSelector:@selector(testMethod2:arg2:)];
    BOOL bool5 = [TestCls instancesRespondToSelector:@selector(testMethod2:arg2:)];

    //(5)クラス名を取得する
    NSString *className = [obj className];

    NSLog(@"%d\n", bool1);
    NSLog(@"%d\n", bool2);
    NSLog(@"%d\n", bool3);
    NSLog(@"%d\n", bool4);
    NSLog(@"%d\n", bool5);
    NSLog(@"%@\n", className);

    [pool drain];
    return 0;
}
main.m

 (1)のisMemberOfClass:メソッドは、そのオブジェクトがあるクラスのインスタンスであるかどうかを判定します。引数には、“あるクラス”を表すクラスオブジェクトを渡します。

 クラスオブジェクトは、あるクラスの型としてのメタ情報を保持するオブジェクトです。上記の例では、NSObjectの静的メソッドであるclassメソッドで取得しています。クラスオブジェクト自身の型としてはid型を利用するか、または文字通りClassという型を利用します。以下にクラスオブジェクトの生成方法を示します。

Class cls1 = [TestCls class];                  // 静的メソッドで
Class cls2 = [obj class];                      // インスタンスメソッドで
Class cls3 = NSClassFromString(@"TestCls");    // 文字列から

 (2)のisKindOfClass:メソッドは、そのオブジェクトがあるクラスのサブクラスかどうかを判定します。引数は判定の基準となるスーパークラスのクラスオブジェクトです。

 (3)のconformsToProtocol:メソッドは、そのオブジェクトがあるプロトコルを採用しているかどうかを判定します。引数は、今度はプロトコルを表すProtocol型のオブジェクトです。プロトコルオブジェクトは、上記のように「@protocol(プロトコル)」で取得するほか、「NSProtocolFromString(@"ProtocolA")」のように文字列から取得する方法もあります。

 (4)のrespondsToSelector:メソッドは、そのオブジェクトがあるメソッドを実装しているかどうかを判定します。instancesRespondToSelector:メソッドは、具体的なオブジェクトではなく、あるクラスが、あるメソッドを実装しているかを判定します。

 “あるメソッド”を表現するためには、SEL型を利用します。Objective-Cでは、メッセージ式で利用されるメソッド名をセレクタとも呼びます。このセレクタを表現するための型がSEL型です。

 セレクタオブジェクトは、上記例のように「@selector()」で取得するほか、「NSSelectorFromString(@"testMethod2:arg2:")」のように文字列から取得することもできます。

 (5)のclassNameは、オブジェクトからクラス名を取得するメソッドです。

クラスクラスタ

 先ほど紹介したisMemberOfClassやclassNameのようにオブジェクトのクラス名を判定する場面では、注意しなければならないことが1つあります。それは、あるクラスのオブジェクトを生成したつもりでも、実はそのサブクラスのインスタンスが返される場合があるということです。

 実装者側からは1つのクラス定義に見えても、内部で状況に応じて複数のサブクラスが使い分けられている場合があります。Objective-Cのこのような仕組みをクラスクラスタといいます。

 クラスクラスタの代表的な例はFoundationのNSStringクラスです。以下のコードを実行してみましょう。

#import <Foundation/Foundation.h>

int main(void) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

        NSString *str1 = @"aiueo";
        NSLog(@"str1 is %@\n", [str1 className]);

        NSString *str2 = [@"/" stringByAppendingPathComponent:@"Users"];
        NSLog(@"str2 is %@\n", [str2 className]);

    [pool drain];
    return 0;
}
main.m

 各文字列オブジェクトからclassNameメソッドでクラス名を取得してみると、「@"aiueo"」で生成したオブジェクトでは「NSCFString」が、stringByAppendingPathComponent:メソッドで生成したオブジェクトでは「NSPathStore2」が、それぞれ返されます。

 これは、インスタンス生成時に利用されたイニシャライザやインスタンス生成メソッドによって、それぞれふさわしいサブクラスが選定され、そのインスタンスが返されているのです。これらのサブクラスは、実装者側には隠ぺいされており、通常は意識することはありません。

 このような、クラスクラスタを構成するサブクラスのことをコンクリートクラスといいます。

 なお、@"aiueo"という最もシンプルな方法で生成された文字列オブジェクトがNSStringではなくNSCFStringとなっているのには理由があります。

 Mac OSには、Cocoaのほかに、旧OSとの互換性を維持したCarbonというフレームワークがあります。また、このCarbonの基本ライブラリ(CocoaのFoundationに該当するもの)としてCore Foundationというものがあります。

 このCore Foundationの文字列クラスであるCFStringを、Cocoa のFoundationから利用できるようにラッピングしているのがNSCFStringクラスなのです。そして、NSCFStringをコンクリートクラスの1つとして、CocoaのNSStringのクラスクラスタが構成されているわけです。

 このように、Objective-Cのクラス構成は内部的に少し複雑になっている場合があります。ただし、先ほども述べたようにクラスクラスタのコンクリートクラスは実装者側には隠ぺいされていますので、普段は意識する必要はありません(むしろ意識させないためにクラスクラスタのような仕組みがあるのです)。ですが、クラスの型を直接判定してロジックに反映させるような場面では、注意が必要になります。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。