【コラム】

ダイナミックObjective-C

44 AspectCocoa (2) - IMPによるアスペクト指向の実現

44/121

前回より、Cocoaでアスペクト指向を実現する、AspectCocoaの紹介を行なっている。今回は、まず、アスペクト指向を実現するために必要な条件を考察しよう。そして、それがどのようにObjective-Cで実現されているかを、紹介していく。

アスペクト指向とは?

始めに、簡単にアスペクト指向について、簡単に説明しておこう。ただし、ここでは極めて簡単な説明に留めるので、詳しくはアスペクト指向を解説した書籍やWebページをあたってほしい。

アスペクト指向を一言で説明すれば、様々なモジュールから共通して利用される機能を、まとめておくプログラミング技法、ということになる。もちろん、この説明だけでは、通常のライブラリとの違いは分かりにくい。アスペクト指向の特徴的な点は、この機能の呼び出し方にある。

例として、アスペクト指向で取り上げられる機能の代表である、ログ出力で説明しよう。あるアプリケーションで、実行中の様々なタイミングで、ログを出力したいたとする。従来のアプローチでは、ログ出力のためのライブラリを作り、それをアプリケーションから、適切なタイミングで呼び出す事になる。

通常のログ出力

分かりやすいが、問題もある。少しログ出力を行なってみればすぐに気づく事だが、アプリケーションのソースコードのあちらこちらに、ログのためのコードを埋め込むのは非常に面倒くさい。また、アプリケーションを出荷する際には、ログのコードを取り除く事もあるだろう。すべてのソースコードを走査してログ出力部を削除するのは、大変な労力が必要になる。

そこで、発想を変えてみよう。アプリケーション本体からログ出力を呼び出すのではなく、ログ出力機能の方から、アプリケーションに処理を埋め込んでやるのだ。埋め込む位置は、プログラミングで決定できるようにする。たとえば、「Helloオブジェクトのhelloメソッドを実行する直前」や、「すべてのメソッドの実行直後」といった風な、指定があるだろう。

ログ出力からのアプリケーションへの埋め込み

これなら、アプリケーションの本体のソースコードを変更する事なく、機能を追加できる。また、機能の取り外しも簡単だ。このような機能の埋め込みのことを、「織り込み(ウィーブ)」と呼ぶ。また、複数のモジュールに織り込み可能な機能を、「横断的な機能」と呼ぶ。これがアスペクト指向の、基本的な考え方になる。

織り込みの実現

さて、アスペクト指向の発想は理解できたとして、次の問題は、これをどのように実現するか、ということになる。そこで、オブジェクト指向言語、つまりクラスとメソッドから構成されているプログラミング環境に、アスペクト指向を導入する事を考えてみよう。

このような環境で、織り込みをどこに行なうか、ということを考えてみる。真っ先に思いつくのは、メソッドの実行であろう。メソッドは実行処理の単位であるから、メソッド実行の直前と直後に、織り込みを行なうポイントを指定するのが有効であろう。他にも、メソッドの呼び出しや、インスタンス変数への値の設定、例外ハンドラの呼び出し、などが考えられる。

つまり、これらが実行される直前や直後に、任意のメソッドを呼び出す事が出来ればいいわけだ。これで、アスペクト指向実現のために必要なものが、明確になった。

Objective-Cでの織り込み

AspectCocoaでは、Objective-Cでこれらを実現している。Objective-Cでは、本連載で何度も取り上げて来たように、メソッドの追加や実装の置き換えが、とても自由に行なえる。AspectCocoaも、この特性を利用している。

だがこの故に、AspectCocoaが実現しているアスペクト指向は、AspectJに代表される実装とは、少し違うものになっている。まず第一に、アスペクト指向のための、新しい言語仕様というものは必要ない。Objective-Cの文法だけで実現しているのだ。さらに、織り込みを行なう際に、ターゲットとなるアプリケーションの再コンパイルは必要ない。ソースコードすら必要ないのである。これは、Objective-Cのランタイムが十分な情報を提供してくれるためだ。

ただし、AspectCocoaが提供する機能は、AspectJのものよりは限られる。特に、織り込みを行なう事が出来るポイントは、大きく制限される事になる。ここは、残念なところだ。

IMPの交換による織り込みの実現

AspectCocoaの基本的なアイディアは、「メソッドの置換」だ。あるメソッドの呼び出しをラップして、もとのメソッドを呼び出す前後に、任意の処理を挿入できるようにしている。したがって、その実現は置き換えの方法に依存する事になる。

メソッドを置換する方法として、4つの方法が提案されている。これらを紹介しよう。

まず1つ目は、メソッドの実装であるIMPを交換する方法だ。Objective-Cでは、メソッドはMethod構造体で表し、その処理の実体として、C関数のポインタであるIMPを持つ。このIMPをすげ替える事で、メソッドの処理を交換する事が出来る。

これを利用する事で、メソッド実行の前後に、処理を埋め込む。たとえば、NSObjectにmethodAというメソッドがあるとする。このメソッドを呼ぶときに、その前後で、"beofre"と"after"という文字列を表示させてみよう。

そのために、new_methodAというメソッドを用意する。この中で、"before"と"after"の表示を行ない、もとのmethodAの実装を呼ぶようにする。そして、methodAとnew_methodAの実装を交換してやるのだ。交換するコードは、次のようになる。

Method old, new;
old = class_getInstanceMethod([NSObject class], @selector(methodA));
new = class_getInstanceMethod([NSObject class], @selector(new_methodA));

IMP swap;
swap = old->method_imp;
old->method_imp = new->method_imp;
new->method_imp = swap;

new_methodAの実装は、次のようにしよう。まず"before"の表示を行なう。そして、new_methodAを呼び出す。このとき、メソッドの実装は交換されているので、実際はもとのmethodAの実装が呼び出されることになる。その後に、"after"を表示している。

- (void)new_methodA
{
    NSLog(@"before");
    [self new_methodA];
    NSLog(@"after");
}

この結果、methodAを呼び出した時の処理の流れは、次の図のようになる。

メソッドの交換によるbeforeとafterの実現

まず、methodAを呼び出す(1)。だが実装が交換されているので、実際に呼び出されるのは、new_methodAの実装になる(2)。new_methodAの実装の内部では、まず"before"を表示する。そして、new_methodAを呼び出す(3)。new_methodAは、交換された、もとのmethodAの実装を呼び出す(4)。最後に、"after"を表示して終了となる。

このような方法で、メソッド実行の前後に、処理の織り込みを実現できるのだ。Objective-Cの、メソッドとその実装が分離しているという特徴をうまく使っている事が分かる。

次回は、2つ目以降の実現方法を紹介しよう。

44/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歩

もっと見る



人気記事

一覧

イチオシ記事

新着記事