踊る犬.netブログ (旧)

[Objective-C] 任意のクラスのサブクラスをランタイムで列挙する方法

プラグイン的なアーキテクチャでファクトリ部が肥大化する問題

複数種類のファイルを取り扱うようなアプリを思い浮かべてみて下さい。
対応するファイル形式を柔軟に増やせるようなアーキテクチャを、Objective-Cではどう構築するのが良いでしょう?
各形式を取り扱うモジュールがあって、それらは定型化された仕様に則って振る舞うような一般的なsubclassingモデルと、ファクトリパターンが良さそうです。

しかし、取り扱える種類が増えると、モジュールを統括する部分(ファクトリ部)の実装が肥大化する問題が発生します。
例えば、指定した拡張子に対応するクラスのインスタンスを返す関数とか。
更に、新しい形式を追加した時に、ファクトリ部への追加実装の手間も後々に負担となったり、実装漏れが生ずる可能性があります。

モジュールをランタイムで列挙してスッキリさせる

ファクトリ部をスッキリさせるには、通常のsubclassingモデルよりももう少し粗結合な実装方法を検討してみると解決するかもしれません!
それは、基底クラスのサブクラスの列挙部をハードコーディングするのではなく、ランタイムで列挙する方法です。
サブクラスに、ファクトリ部で取り扱いが必要な情報を提供するGetterメソッドやプロパティを持たせます。

これなら、新モジュールの登録忘れや一貫性を崩す心配を軽減できます!

ランタイムAPIを使って実現

NSObjectに対してメソッドを追加するカテゴリを紹介します。
追加されるメソッドの+ classNamesForSubclasses を、あるクラスをレシーバにして呼び出すと、その子クラスの名前がNSArrayで返ります。
チョー簡単ですね!

ヘッダファイル(NSObject+AutomaticFactory.h):

#!objectivec
#import <Foundation/Foundation.h>

@interface NSObject (AutomaticFactory)

+ (NSArray*) classNamesForSubclasses;

@end

ソースファイル(NSObject+AutomaticFactory.m):

#!objectivec

#import "NSObject+AutomaticFactory.h"
#import <objc/runtime.h>

@implementation NSObject (AutomaticFactory)

+ (NSArray*) classNamesForSubclasses;
{

    int numClasses = 0, newNumClasses = objc_getClassList(NULL, 0);
    Class *classList = NULL;

    while (numClasses < newNumClasses) {
        numClasses = newNumClasses;
        classList = (Class*)realloc(classList, sizeof(Class) * numClasses);
        newNumClasses = objc_getClassList(classList, numClasses);
    }

    NSMutableArray *classesArray = [NSMutableArray array];

    for (int i = 0; i < numClasses; i++) {
        Class superClass = classList[i];
        do {
            // recursively walk the inheritance hierarchy
            superClass = class_getSuperclass(superClass);
            if (superClass == [self class]) {
                [classesArray addObject:NSStringFromClass(classList[i])];
                break;
            }
        } while (superClass);
    }

    free(classList);

    return classesArray;
}
@end

プラグイン的な実装をしている場合は、検討してみてはいかがでしょうか?

参考ページ: Automagic Factories in Objective-C