【コラム】

ダイナミックObjective-C

105 Fast Enumeration(1) - 速い列挙子

105/121

ここまで、Objective-C 2.0の新要素として、ガベージコレクションとプロパティと取り上げてきた。今回からは3つめの要素となる、Fast Enumerationを取り上げよう。

Fast Enumerationの書き方

「配列やリストといったコレクションの、すべての要素にアクセスするにはどうしたらいいか?」というのは、プログラミングの世界では古くからある問題だ。

C言語を振り返ってみよう。C言語には配列がある。配列にアクセスする一般的な方法は、for文と添字を使うものだ。次のような書き方になるだろう。

リスト1

int array[10];

...

int i, value;
for (i = 0; i < 10; i++) {
    // 添字を使って要素を取り出す
    value = array[i];
    ...
}

このようなfor文を使った書き方は、C言語の配列では有利だ。それは、C言語の配列は、連続したメモリブロックとして確保されるため、添字を使う場合でも速いアクセスが保証されるからだ。

だがリンクリストのようなデータ構造の場合だと、添字によるアクセスは遅くなる。そこで導入されるのが列挙子だ。列挙子は、コレクションの先頭から順々に、「その次」の要素を取り出すための仕組みだ。

Cocoa + Objective-Cでは、列挙子を表すクラスとしてNSEnumeratorを提供している。配列を表すNSArrayや辞書を表すNSDictionaryなどから、NSEnumeratorを取得することが出来る。NSEnumeratorをwhile文と組み合わせて、すべての要素にアクセスすることが出来るのだ。

リスト2

NSArray* array;

...


// 列挙子を取得する
NSEnumerator* enumerator;
enumerator = [array objectEnumerator];

// while文を使って要素にアクセスする
id object;
while (object = [enumerator nextObject]) {
    ...
}

これならばリンクリストのような構造でも速くアクセスできる。また、コレクションクラスと、そこにアクセスするためのクラスを切り分けたことで、どのようなコレクションに対しても同じ書き方が出来るという利点が生まれた。

いいことづくしのように見えるNSEnumeratorだが、その欠点はソースコードの記述量が多いことにある。NSEnumerator型の変数を1個余分に宣言しなくてはいけないし、なによりこの「NSEnumerator」というクラス名が長い。プログラミングの最中では頻繁に出てくる書き方なので、煩わしさを感じることもある。

そこでObjective-C 2.0から導入されたのが、Fast Enumerationだ。これは列挙子をより使いやすくするためのものだ。文法としては、for文を拡張している。いろいろ説明するよりも、見てもらった方が早いだろう。NSArrayの要素にアクセスする、Fast Enumerationとfor文を使ったソースコードは、次のようになる。

リスト3

NSArray* array;

...

// for文を使ってNSArrayの要素にアクセスする
for (id object in array) {
    ...
}

こういうことだ。C言語の書き方に近い。とても直感的ではないだろうか。

for文の中を見てみると、先頭に一時変数の宣言があり、その後ろにinというキーワードがある。このinの後ろに、配列などのコレクションクラスを置くのだ。

列挙子の利点を活かしながら、記述するソースコードの量を確実に減らす。それがFast Enumerationの目的だ。

逆方向へのアクセス

Fast Enumerationで、for文のinの後ろに置くことが出来るのは、NSArrayだけではない。実は、NSEnumeratorを置くことも出来るのだ。これを活用して、NSArrayの先頭からだけではなく、最後尾から先頭に向かってアクセスするようにすることも出来る。

どうするかというと、NSArrayには列挙子を取得するメソッドとしてobjectEnumeratorがあるが、もう1つreverseObjectEnumeratorというメソッドもある。これを使うと、名前から予想がつくと思うが、順番をひっくり返してアクセス出来るのだ。

reverseObjectEnumeratorが必要になるのは、配列から要素を削除するときだ。たとえば、配列からある特定の要素を削除する、という処理を考えてみよう。単純に書くと、こうなる。

リスト4

for (id object in array) {
    // objectを削除する必要があるかどうかチェックする
    if ([object shoudRemove]) {
        // objectを削除する
        [array removeObject:object];
    }
}

このソースコードには問題がある。実際に走らせてみると、「Collection was mutated while being enumerated.」という例外が発生する。つまり、「列挙子を用いてアクセスしている最中に、配列の中身を変更するな」ということだ。

これは、配列の構造を考えればわかる。配列の要素を削除すると、それより後ろにある要素すべてが、「1つ前」へ移動することになる。「その次」の要素をたぐっていくNSEnumeratorにとっては、都合が悪い。

この問題を回避するには、1回配列をコピーして、列挙子を取り出す配列と削除を行う配列とを分ける、という方法もあるが煩わしい。

そこで使うのが、reverseObjectEnumeratorだ。これを使って、逆向きに走査を行えばいい。

リスト5

// 逆方向にアクセスする
for (id object in [array reverseObjectEnumerator]) {
    if ([object shoudRemove]) {
        [array removeObject:object];
    }
}

この書き方だと、先ほどの例外は発生しない。要素を削除して配列の状態が変わったとしても、これからアクセスする要素には影響がないからだ。これで要素の削除も、効率的に記述することができる。

今回はFast Enumerationの使い方について説明した。次回は、その実装の仕組みに踏み込んでみよう。

105/121

インデックス

連載目次
第121回 デザインパターンをObjective-Cで - Visitor (2)
第120回 デザインパターンをObjective-Cで - Visitor (1)
第119回 デザインパターンをObjective-Cで - Template Method (1)
第118回 デザインパターンをObjective-Cで - Strategy (1)
第117回 デザインパターンをObjective-Cで - State (1)
第116回 デザインパターンをObjective-Cで - Interpreter (2)
第115回 デザインパターンをObjective-Cで - Interpreter (1)
第114回 デザインパターンをObjective-Cで - Mediator (3)
第113回 デザインパターンをObjective-Cで - Mediator (2)
第112回 デザインパターンをObjective-Cで - Mediator (1)
第111回 デザインパターンをObjective-Cで - Observer (3)
第110回 デザインパターンをObjective-Cで - Observer (2)
第109回 デザインパターンをObjective-Cで - Observer (1)
第108回 Fast Enumeration (4) - Fast Enumerationに対応するクラスの実装
第107回 Fast Enumeration (3) - Fast Enumerationのソースコード
第106回 Fast Enumeration(2) - NSFastEnumerationプロトコル
第105回 Fast Enumeration(1) - 速い列挙子
第104回 プロパティ(4) - プロパティの属性
第103回 プロパティ(3) - ドット演算子
第102回 プロパティ(2) - プロパティの宣言
第101回 プロパティ(1) - インスタンス変数のアクセス制御
第100回 ガベージコレクション(5) - コピーGCとコンパクション
第99回 ガベージコレクション (4) - マーク・アンド・スイープ
第98回 ガベージコレクション(3) - 保守的でありながらオブジェクト的
第97回 ガベージコレクション (2) - 実体であるlibauto
第96回 ガベージコレクション (1) - GCのためのAPI
第95回 デザインパターンをObjective-Cで - Memento (2)
第94回 デザインパターンをObjective-Cで - Memento (1)
第93回 デザインパターンをObjective-Cで - Chain of Responsibility (5)
第92回 デザインパターンをObjective-Cで - Chain of Responsibility (4)
第91回 デザインパターンをObjective-Cで - Chain of Responsibility (3)
第90回 デザインパターンをObjective-Cで - Chain of Responsibility (2)
第89回 デザインパターンをObjective-Cで - Chain of Responsibility (1)
第88回 デザインパターンをObjective-Cで - Command (5)
第87回 デザインパターンをObjective-Cで - Command (4)
第86回 デザインパターンをObjective-Cで - Command (3)
第85回 デザインパターンをObjective-Cで - Command (2)
第84回 デザインパターンをObjective-Cで - Command (1)
第83回 デザインパターンをObjective-Cで - Iterator (2)
第82回 デザインパターンをObjective-Cで - Iterator (1)
第81回 デザインパターンをObjective-Cで - Proxy (3)
第80回 デザインパターンをObjective-Cで - Proxy (2)
第79回 デザインパターンをObjective-Cで - Proxy (1)
第78回 デザインパターンをObjective-Cで - Flyweight (2)
第77回 デザインパターンをObjective-Cで - Flyweight (1)
第76回 デザインパターンをObjective-Cで - Facade (1)
第75回 デザインパターンをObjective-Cで - Decorator (2)
第74回 デザインパターンをObjective-Cで - Decorator (1)
第73回 デザインパターンをObjective-Cで - Composite (2)
第72回 デザインパターンをObjective-Cで - Composite (1)
第71回 デザインパターンをObjective-Cで - Bridge (3)
第70回 デザインパターンをObjective-Cで - Bridge (2)
第69回 デザインパターンをObjective-Cで - Bridge (1)
第68回 デザインパターンをObjective-Cで - Web Kitを考える Adapter (4)
第67回 デザインパターンをObjective-Cで - Adapater(3)
第66回 デザインパターンをObjective-Cで - Adapater (2)
第65回 デザインパターンをObjective-Cで - Adapter (1)
第64回 デザインパターンをObjective-Cで - Factory Method (4)
第63回 デザインパターンをObjective-Cで - Factory Method (3)
第62回 デザインパターンをObjective-Cで - Factory Method (2)
第61回 デザインパターンをObjective-Cで - Factory Method (1)
第60回 デザインパターンをObjective-Cで - Prototype (4)
第59回 デザインパターンをObjective-Cで - Prototype (3)
第58回 デザインパターンをObjective-Cで - Prototype (2)
第57回 デザインパターンをObjective-Cで - Prototype (1)
第56回 デザインパターンをObjective-Cで - Builder (2)
第55回 デザインパターンをObjective-Cで - Builder (1)
第54回 デザインパターンをObjective-Cで - Abstract Factory (2)
第53回 デザインパターンをObjective-Cで - Abstract Factory (1)
第52回 デザインパターンをObjective-Cで - Singleton (3)
第51回 デザインパターンをObjective-Cで - Singleton (2)
第50回 デザインパターンをObjective-Cで - Singleton (1)
第49回 デザインパターンで読み解くCocoa
第48回 F-Script - CocoaとObjective-Cのスクリプティング環境
第47回 AspectCocoa (5) - インプットマネージャとの連携
第46回 AspectCocoa (4) - AspectCocoaの実例
第45回 AspectCocoa (3) - フォワーディングとポージングの利用
第44回 AspectCocoa (2) - IMPによるアスペクト指向の実現
第43回 AspectCocoa (1) - Objective-CとCocoaによるアスペクト指向
第42回 SIMBLでハックを管理
第41回 インプットマネージャから侵入
第40回 Toll-free bridge (3) - Objective-Cメソッドの処理
第39回 Toll-free bridge (2) - Core Foundationのisaフィールド
第38回 Toll-free bridge(1) - 変換コスト0のブリッジ
第37回 Core Foudation (5) - インスタンスの実装
第36回 Core Foudation (4) - 多態性の実現
第35回 Core Foundation(3) - クラスの定義
第34回 Core Foundation(2) - C言語によるオブジェクト
第33回 Core Foundation(1) - Core Foundation誕生前夜
第32回 抽象クラスとクラスクラスタ
第31回 ランタイムAPIでさらに動的に(5) - インスタンス変数に動的にアクセス
第30回 ランタイムAPIでさらに動的に(4) - インスタンス変数の定義を調査
第29回 ランタイムAPIでさらに動的に(3) - メソッドの実装の置換
第28回 ランタイムAPIでさらに動的に(2) - メソッドの追加
第27回 ランタイムAPIでさらに動的に(1) - 動的なクラスの作成
第26回 メッセージ送信(4) - メッセージ送信の流れと関数呼び出しとの違い
第25回 メッセージ送信(3) - メソッドのキャッシング
第24回 メッセージ送信(2) - メソッドリストからメソッドを検索する
第23回 メッセージ送信(1) - objc_msgSendの実装
第22回 メソッドとは何か(5) - メソッドの実装
第21回 メソッドとは何か(4) - セレクタの実体
第20回 メソッドとは何か(3) - メソッドの型を読み解く
第19回 メソッドとは何か(2) - メソッドを取得する
第18回 メソッドとは何か(1) - メソッド、セレクタ、メソッドの実装
第17回 クラスとは何か(4) - Objective-Cにおけるオブジェクトとは何か?
第16回 クラスとは何か(3) - メタクラスと親クラス
第15回 クラスとは何か(2) - クラス情報に直接アクセスする
第14回 クラスとは何か(1) - Mac OS X/Objective-Cにおけるクラスの実装を読む
第13回 Objective-Cのエンジン部 - ランタイムに踏み込む
第12回 ポージングで乗っ取り
第11回 2つのプロトコルの使い分け
第10回 非形式プロトコル - もう1つのプロトコル
第9回 プロトコルが必要とされた背景とは? - なぜあえて静的な型を?
第8回 カテゴリ - 動的なメソッドの追加によるクラスの拡張
第7回 Objective-Cと様々な言語のブリッジ - PyObjC、RubyCocoa……
第6回 Cocoa-Javaの挑戦とは? - 似て非なるセレクタとリフレクション
第5回 ターゲット/アクションパラダイム(2) - その利点を徹底検証
第4回 ターゲット/アクションパラダイム(1) - 動的特性を利用したデザインパターン
第3回 Cocoa実現の肝 - クラスとそのメソッドの調査方法をチェック
第2回 Objective-Cの動的型付け
第1回 CocoaとObjective-Cと動的なオブジェクト指向 - Cocoaハックの第1歩

もっと見る



人気記事

一覧

イチオシ記事

新着記事