【iOS/Objective-c】Method Swizzling
追記しました 2014/12/29
【iOS/Objective-c】Method Swizzling 修正版 - ふるすたっくえんじにあっぽい人の日記
今日はMethod Swizzlingだぉ
いわゆるメソッドのフックだぉ
世間では黒魔術と呼ばれてるぉ
これとかカテゴリとかがあるから、なおさらObjective-cでライブラリとか使いたくないんだぉ
ググると +(void)load内でmethod_exchangeImplementationsしてる人がいるようですがね
loadのタイミングだと[UIApplication sharedApplication]がnilですがね
いや、ほんとはわたくしもloadに書きたいんですがね
今回晒すのはAppDelegateで呼ばれるメソッドたちをフックする例
拡張性もなにもいらないんで、適当
AppDelegateのdidFinishLaunchingWithOptions内で[Hoge sharedInstance]を呼び出してもらえれば
swizzled AppDelegate Methodsの下を見るとどーみても無限ループにしか見えないんだぉ
実際はexchangeされてるから、AppDelegateのメソッドが呼ばれるんだぉ
ちなみにもちろんこのswizlled Method内ではselfはAppDelegateになるから要注意だぉ
ここらへんが黒魔術と呼ばれる所以だぉ
カオスになるからお子ちゃまはMethod Swizzling使っちゃだめだぉ
#pragma mark - Initialize + (Hoge *)sharedInstance { static Hoge *instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[Hoge alloc] initInstance]; }); return instance; } - (id)init { [self doesNotRecognizeSelector:_cmd]; return nil; } - (id)initInstance { self = [super init]; if (self) { [self swizzleAppDelegateMethods]; } return self; } #pragma mark - Private Methods /*! @brief AppDelegateのメソッドを自クラスのメソッドと差し替え */ - (void)swizzleAppDelegateMethods { [self swizzleMethod:@selector(applicationWillResignActive:)]; [self swizzleMethod:@selector(applicationDidEnterBackground:)]; [self swizzleMethod:@selector(applicationWillEnterForeground:)]; [self swizzleMethod:@selector(applicationDidBecomeActive:)]; [self swizzleMethod:@selector(applicationWillTerminate:)]; [self swizzleMethod:@selector(application:didReceiveRemoteNotification:)]; } - (void)swizzleMethod:(SEL)sel { Method originalMethod = class_getInstanceMethod([[UIApplication sharedApplication].delegate class], sel); Method altMethod = class_getInstanceMethod([self class], sel); method_exchangeImplementations(originalMethod, altMethod); } #pragma mark - swizzled AppDelegate Methods - (void)applicationWillResignActive:(UIApplication *)application { [[Hoge sharedInstance] applicationWillResignActive:application]; } - (void)applicationDidEnterBackground:(UIApplication *)application { [[Hoge sharedInstance] applicationDidEnterBackground:application]; } - (void)applicationWillEnterForeground:(UIApplication *)application { [[Hoge sharedInstance] applicationWillEnterForeground:application]; } - (void)applicationDidBecomeActive:(UIApplication *)application { [[Hoge sharedInstance] applicationDidBecomeActive:application]; } - (void)applicationWillTerminate:(UIApplication *)application { [[Hoge sharedInstance] applicationWillTerminate:application]; } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { [[Hoge sharedInstance] application:application didReceiveRemoteNotification:userInfo]; }