開發背景
利用adobe air開發完游戲後,需要針對ios或者android平台進行支付、推送的sdk接入,本文可以用來徹底解決ios平台下delegate生命周期幾個回調函數的調用,實現原生的推送、支付功能的接入
hook知識背景
(objc裡的Method Swizzling,本節內容轉自http://blog.csdn.net/yiyaaixuexi)
在Objective-C中調用一個方法,其實是向一個對象發送消息,查找消息的唯一依據是selector的名字。利用Objective-C的動態特性,可以實現在運行時偷換selector對應的方法實現,達到給方法掛鉤的目的。
每個類都有一個方法列表,存放著selector的名字和方法實現的映射關系。IMP有點類似函數指針,指向具體的Method實現。
我們可以利用 method_exchangeImplementations 來交換2個方法中的IMP,
我們可以利用 class_replaceMethod 來修改類,
我們可以利用 method_setImplementation 來直接設置某個方法的IMP,
……
歸根結底,都是偷換了selectZ喎?/kf/ware/vc/" target="_blank" class="keylink">vcrXESU1Qo6zI58/CzbzL+cq+o7o8YnI+CjwvcD4KPHA+PGltZyBzcmM9"/uploadfile/Collfiles/20141111/2014111108225471.png" alt="\">
實現思路
1、在MDChangeDelegateHelper類加載階段就對adboe air的appdelegate類進行方法hook,確保在adobe air的appdelegate在創建前替換成我們新實現的類
2、需要3個SEL,
一個AppDelegate原SEL:oldSEL,
一個MDChangeDelegateHelper的默認SEL:defaultSEL,用於為原appdelegate添加默認的原oldSEL
一個MDChangeDelegateHelper的目標SEL:newSEL
方法替換思路:
3、替換後對應的sel關系為
oldSEL -- newMethod
newSEL -- oldMethod
所以當對應的appdelegate方法被調用時,
oldSEL找到了newMethod的實現,newMethod的實現在MDChangeDelegateHelper.m內的hookedxxx方法
在newMethold中調用了newSEL,newSEL指向oldMethod,實現了原流程的兼容
代碼實現
// // MDChangeDelegateHelper.m // ChangeDelegateDemo // // Created by ashqal on 14-10-31. // Copyright (c) 2014年 moredoo. All rights reserved. // #import "MDChangeDelegateHelper.h" #import#import void hookMethod(SEL oldSEL,SEL defaultSEL, SEL newSEL) { //CTAppController Class aClass = objc_getClass("CTAppController"); if ( aClass == 0 ) { NSLog(@"!!!!!!!!!!!!!did not find adobe class!"); return; } Class bClass = [MDChangeDelegateHelper class]; //把方法加給aClass class_addMethod(aClass, newSEL, class_getMethodImplementation(bClass, newSEL),nil); class_addMethod(aClass, oldSEL, class_getMethodImplementation(bClass, defaultSEL),nil); Method oldMethod = class_getInstanceMethod(aClass, oldSEL); assert(oldMethod); Method newMethod = class_getInstanceMethod(aClass, newSEL); assert(newMethod); method_exchangeImplementations(oldMethod, newMethod); } @implementation MDChangeDelegateHelper + (void)load { NSLog(@"MDChangeDelegateHelper load"); //[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(createStarterNotificationChecker:) //name:@"UIApplicationWillFinishLaunchingNotification" object:nil]; //[self changeDelegate]; hookMethod( @selector(application:didFinishLaunchingWithOptions:) ,@selector(defaultApplication:didFinishLaunchingWithOptions:) ,@selector(hookedApplication:didFinishLaunchingWithOptions:) ); hookMethod( @selector(application:handleOpenURL:) , @selector(defaultApplication:handleOpenURL:) , @selector(hookedApplication:handleOpenURL:) ); hookMethod(@selector(application:openURL:sourceApplication:annotation:) , @selector(defaultApplication:openURL:sourceApplication:annotation:) , @selector(hookedApplication:openURL:sourceApplication:annotation:)); hookMethod(@selector(application:supportedInterfaceOrientationsForWindow:) , @selector(defaultApplication:supportedInterfaceOrientationsForWindow:) , @selector(hookedApplication:supportedInterfaceOrientationsForWindow:) ); hookMethod(@selector(applicationDidBecomeActive:) , @selector(defaultApplicationDidBecomeActive:) , @selector(hookedApplicationDidBecomeActive:) ); hookMethod(@selector(init) , @selector(init) , @selector(hookedInit) ); } -(BOOL)hookedApplication:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)dic { NSLog(@"hooked didFinishLaunchingWithOptions"); [self hookedApplication:application didFinishLaunchingWithOptions:dic]; return YES; } -(id)hookedInit { NSLog(@"hooked init!!!"); return [self hookedInit]; } - (BOOL)hookedApplication:(UIApplication *)application handleOpenURL:(NSURL *)url { NSLog(@"hooked handleOpenURL"); [self hookedApplication:application handleOpenURL:url]; return YES; } - (BOOL)hookedApplication:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { NSLog(@"hooked openURL sourceApplication annotation"); [self hookedApplication:application openURL:url sourceApplication:sourceApplication annotation:annotation]; return YES; } - (NSUInteger) hookedApplication:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window { NSLog(@"hooked supportedInterfaceOrientationsForWindow"); [self hookedApplication:application supportedInterfaceOrientationsForWindow:window ]; return 0; } - (void)hookedApplicationDidBecomeActive:(UIApplication *)application { [self hookedApplicationDidBecomeActive:application]; NSLog(@"hooked applicationDidBecomeActive"); } - (BOOL)defaultApplication:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)dic{ return YES;} - (BOOL)defaultApplication:(UIApplication *)application handleOpenURL:(NSURL *)url{return YES;} - (BOOL)defaultApplication:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{return YES;} - (NSUInteger) defaultApplication:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{return 0;} - (void)defaultApplicationDidBecomeActive:(UIApplication *)application{} - (id)init { self = [super init]; if (self) { } return self; } @end
a) 在運行時nslog出adobe air的appdelegate名字為CTAppController,所以將此類作為替換對象
b) 在對init函數進行替換時發現CTAppController沒有實現init函數,所以將默認的init函數做了基礎實現,不然無法創建實例了