単純に呼べば使えるAOPライブラリMOAspectsを作った
Objective-CでAOPを実現するライブラリMOAspectsを一昨日ぐらいにCocoapodsに登録してきた。
MO-AI/MOAspects
https://github.com/MO-AI/MOAspects
AOPとは?
アスペクト指向(Aspect Oriented Programming)の略、難しい言葉だと横断的関心事の分離
と言われている。要はロギングのような全体に散らばる処理を一括で管理できるようにしたことをアスペクトという。
なぜ作ったか?
あるときGoogleAnalyticsの埋め込みをやることになり画面毎、アクション毎にGoogleAnalytics用の定義を切る必要があったが、1つ1つの該当クラスにそのような対応を行うのが煩わしいため一括でその定義を切れるクラスが欲しくて作った。
他のAOPライブラリは?
steipete/Aspects
https://github.com/steipete/Aspects
tokorom/BlockInjection
https://github.com/tokorom/BlockInjection
有名なのだと上のようなライブラリがあるが、技術的課題点がいくつかあった。
Aspects
- クラスメソッドに対して処理をフックできない
- 同一クラス系統に対して複数処理をフックできない
- UIViewController/UITableViewControllerなど
BlockInjection
- 64bit対応でない
- 同一クラス系統に対して複数処理をフックできない
このライブラリの売り所
- 32bit/64bit対応
- インスタンスメソッド/クラスメソッド対応
- 同一クラス系統のクラスに対して複数フックが可能
表にするとこんな感じ
ライブラリ名 | 64bit対応 | インスタンス メソッド |
クラス メソッド |
同一系統 クラスフック |
---|---|---|---|---|
Aspects | ○ | ○ | × | × |
BlockInjection | × | ○ | ○ | × |
MOAspects | ○ | ○ | ○ | ○ |
とにかく実装上の都合を開発者に対して気を使わせないようなライブラリを目的とした。インターフェースとしてインスタンスメソッド・クラスメソッドのフック処理の2種類のみ用意しており、引数のClassとSELを基にターゲットを決定する。
インストール
pod 'MOAspects'
インターフェース
// hook instance method
+ (BOOL)hookInstanceMethodForClass:(Class)clazz
selector:(SEL)selector
aspectsPosition:(MOAspectsPosition)aspectsPosition
usingBlock:(id)block;
// hook class method
+ (BOOL)hookClassMethodForClass:(Class)clazz
selector:(SEL)selector
aspectsPosition:(MOAspectsPosition)aspectsPosition
usingBlock:(id)block;
MOAspectsPosition
MOAspectsPositionBefore
とMOAspectsPositionAfter
があり、対象処理の前か後にフックを行うか選択することができる
block
この中にフックする処理を実装する。blockの型の記載がないが以下のようになる
void (^block) (id targetObject, Type1 parameter1, Type2 parameter2...)
使い方
クラスメソッドのフック例
[MOAspects hookClassMethodForClass:[NSNumber class]
selector:@selector(numberWithInt:)
aspectsPosition:MOAspectsPositionBefore
usingBlock:^(id class, int intVar){
NSLog(@"hooked %d number!", intVar);
}];
[NSNumber numberWithInt:10]; // -> hooked 10 number!
インスタンスメソッドのフック例
[MOAspects hookInstanceMethodForClass:[NSString class]
selector:@selector(length)
aspectsPosition:MOAspectsPositionBefore
usingBlock:^(NSString *string){
NSLog(@"hooked %@!", string);
}];
[@"abcde" length]; // -> hooked abcde!
Objective-Cな理由
Swiftでやろうと思ったが、このライブラリは内部でNSInvocationを使っているため現在SwiftだとNSInvocation is unavailable
と怒られるため止む無くObjective-Cとなった
TODO
- ドキュメント
- ベンチマーク
- 実際にGAと絡めたデモアプリ
最後に
実際に使ってみて元のコードを汚すことなくGoogleAnalyticsを埋め込めたので是非同じような状況になったら使ってみてはいかがでしょうか。