【コラム】

ダイナミックObjective-C

106 Fast Enumeration(2) - NSFastEnumerationプロトコル

    木下誠  [2008/05/12]

    NSFastEnumerationプロトコル

    以前のObjective-Cでは、列挙子を実現するためにNSEnumeratorが使われていた。NSEnumeratorはクラスであり、nextObjectという次のオブジェクトを取得するためのメソッドが定義されていた。

    Objective-C 2.0のFast Enumerationでは、この機能を実現するためにNSFastEnumerationというものを使う。これはプロトコルになる。このプロトコルに準拠しているコレクションクラスに対して、例のfor文による列挙が使えるわけだ。ちなみに、NSArray、NSDictionary、NSSet、NSHashTable、NSMapTableといった、主なコレクションクラスで準拠している。

    NSFastEnumerationプロトコルの定義は、次のようなものになる。

    リスト1

    typedef struct {
        unsigned long   state;
        id*             itemsPtr;
        unsigned long*  mutationsPtr;
        unsigned long   extra[5];
    } NSFastEnumerationState;
    
    @protocol NSFastEnumeration
    - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState*)state objects:(id*)stackbuf count:(NSUInteger)len;
    @end
    

    まず、NSFastEnuemrationStateという構造体が定義されている。これは、列挙の状態を記述するものになる。そして、countByEnumeratingWithState:objects:count:というメソッドがある。これが、Fast Enumerationの中核となるメソッドのはずだ。

    コンパイラが生成するFast Enumerationのためのコード

    さて、このメソッドの使われ方だが、これだけを見ていてもどうにもわからない。ここは真っ当に、Appleが公開しているドキュメントをあたることにしよう。ここからは、『Objective-C 2.0プログラミング言語: 高速列挙』のドキュメントをもとに話を進めていく。

    まずは、このメソッドがどのように使われているかを調べてみよう。for文を使った次のようなソースコード、

    リスト2

    for ( existingItem in expression ) {
        stmts
    }
    

    は、次のようにコンパイルによって変換されることになる。

    リスト3

    {
        id                      elem;
        NSFastEnumerationState  __enumState = { 0 };
        id                      __items[16];
    
        unsigned long           __limit =
                [collection countByEnumeratingWithState:&__enumState objects:__items count:16];
        unsigned                __counter = 0;
        unsigned long           __startMutations = * __enumState.mutationsPtr;
    
        if (__limit) do {
            __counter = 0;
            do {
                if (__startMutations != * __enumState.mutationsPtr) objc_enumerationMutation(); // 引数不足?
                elem = __enumState.itemsPtr[__counter++];
    stmts;
            } while (__counter < __limit);
        } while (__limit = [collection countByEnumeratingWithState:&__enumState objects:__items count:16]);
    };
    

    このソースコードは、先ほどのドキュメントのページからコピーしたものから、若干の文法エラーを修正したものだ。ただし、まだ問題が残っている。14行目にあるobjc_enumerationMutation()の呼び出しだ。これはランタイム関数であるが、ヘッダファイル(objc/runtime.h)によると、プロトタイプ宣言は次のようになっている。

    リスト4

    OBJC_EXPORT void objc_enumerationMutation(id) 
         AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
    

    id型の引数を1つ取るらしい。だが、ソースコードでは引数は指定されていない。この関数は、ランタイムの中でもプライベート関数となるらしく、挙動についての説明がない。よって憶測にならざるを得ないのだが、前後の関係から見て、__enumState.mutationsPtrを渡すことになるのではないだろうか?

    次回は、このソースコードの挙動について調べてみよう。

    関連したタグ

    新着記事

    特設サイトの情報

      求人情報

      人気記事

      一覧

      イチオシ記事

      新着記事

      特別企画

      転職ノウハウ

      あなたの仕事適性診断

      4つの診断で、自分の適性を見つめなおそう!

      Heroes File ~挑戦者たち~

      働くこと・挑戦し続けることへの思いを綴ったインタビュー

      はじめての転職診断

      あなたにピッタリのアドバイスを読むことができます。

      転職Q&A

      転職に必要な情報が収集できます

      スカウト転職する

      企業からアプローチのメッセージが届きます。

      マイナビニュースマガジン