theosのLogo用拡張子は種類がいくつかあるのでその話。
theosって?
theosはiOS/Mac向けのMakefileフレームワークだ。Makefileからincludeしてあげれば苦行のようなMakefileの記述からある程度解放してくれる。
Logo
どちらかと言えばtheosはビルド過程にこの処理を含ませるためのMakefileという側面が強いが、MobileSubstrate向けのswizzleコードの記述を楽に書けるよう変換も行わせる事ができる。 その際の拡張子が以下だ。
- .x
- .xm
- .xi
- .xmi
この拡張子の違いについて説明しよう。まず一番一般的な.xm
だが、これはLogo変換処理を行い.mm
ファイルを生成し、Objective-C++としてプリプロセス、コンパイル、リンクを行う。
次に.x
はそれのObjective-C版だ。全てに言える事だがLogoの変換処理に違いはない。オリジナルのマクロによってObjc/Objc++で普通にswizzleを書くのと同じ状態へ持っていくのだから至極当然の処理順だ。
最後にそれらにi
がついたものだ、これは真っ先にobjc/objc++プリプロセスが行われる。その後にLogo処理、コンパイル、リンクとなる。そこそこコードを書いていると以下のように書きたいと思うこともあるだろう。
#define SHARED_INSTANCE [%c(SBProcess) sharedInstance]
%c
マクロはLogo処理向けであるのでプリプロセスに放り込むとエラーになる。このレベルであればobjc_getClass
で代用がきくが以下のようなケースはそうもいかない。
%hook UIKBKey
- (NSString *)displayString {
NSString *result = %orig();
return [self.name rangeOfString:@"-Small-"].location != NSNotFound ? [result lowercaseString] : result;
}
%end
%hook UIKBTree
- (NSString *)displayString {
NSString *result = %orig();
return [self.name rangeOfString:@"-Small-"].location != NSNotFound ? [result lowercaseString] : result;
%end
処理内容が同じなので変更は一度ですむようにすべきだが、関数にまとめても%orig
は%hook
の外では使用できない。
プリプロセスが先に走る.xi
, .xmi
ではdefineにLogoを書いておく事が可能になるのでdefineでまとめられる。
#define HONOR_CASE \
NSString *result = %orig(); \
return [self.name rangeOfString:@"-Small-"].location != NSNotFound ? [result lowercaseString] : result;
%hook UIKBKey
- (NSString *)displayString { HONOR_CASE }
%end
%hook UIKBTree
- (NSString *)displayString { HONOR_CASE }
%end
めでたく修正時には一度の変更ですむようにできた。
コード出典: ShowCase – github/ashikase
まとめ
extension | process order | |
---|---|---|
.x | Logo –> objc preprocess+compile –> link | |
.xm | Logo –> objc++ preprocess+compile –> link | |
.xi | objc preprocess –> Logo –> objc compile –> link | |
.xmi | objc++ preprocess –> Logo –> objc++ compile –> link |
なおこの表はiphonedevwikiにも記述しておいた。