最近在写debug工具,需要hook一些方法,但是不希望debug工具依赖其他业务库,以前实现这种方式,是通过category,比如需要hook NSURLSessionConfiguration
的defaultSessionConfiguration
方法,用下面的方式实现,没毛病,一直都是这样搞的
@implementation NSURLSessionConfiguration (SKDTDebug)+ (void)load { Method method1 = class_getClassMethod([NSURLSessionConfiguration class], @selector(defaultSessionConfiguration)); Method method2 = class_getClassMethod([NSURLSessionConfiguration class], @selector(LL_defaultSessionConfiguration)); method_exchangeImplementations(method1, method2);}+ (NSURLSessionConfiguration *)LL_defaultSessionConfiguration { NSURLSessionConfiguration *config = [NSURLSessionConfiguration LL_defaultSessionConfiguration]; // ...
return config;}@end
但是,因为是debug工具库,不能依赖其他工具或者业务库,比如我需要hook 网络库里SKIOTCPClient
的sendData
的相关方法,不能通过category的方式,那就可以通过这种方式
#import "SKDTTCPHook.h"#import <objc/runtime.h>#import <objc/message.h>
@implementation SKDTTCPHook+ (void)load{ Class SKIOTCPClientClass = NSClassFromString(@"SKIOTCPClient"); SEL debugSendDataContent = @selector(debugSendDataContent:writeBlock:); SEL sendDataContent = @selector(sendDataContent:writeBlock:); Method debugSendDataContentMethod_SKDTTCPHook = class_getInstanceMethod([SKDTTCPHook class], debugSendDataContent); IMP debugSendDataContentImp_SKDTTCPHook = method_getImplementation(debugSendDataContentMethod_SKDTTCPHook); char *debugSendDataContentTypes_SKDTTCPHook = (char *)method_getTypeEncoding(debugSendDataContentMethod_SKDTTCPHook);
// 给SKIOTCPClient添加SKDTTCPHook的debugSendDataContent方法 BOOL success = class_addMethod(SKIOTCPClientClass, @selector(debugSendDataContent:writeBlock:), debugSendDataContentImp_SKDTTCPHook, debugSendDataContentTypes_SKDTTCPHook); if (success) { // 获取SKIOTCPClient里的debugSendDataContent方法 Method debugSendDataContentMethod_SKIOTCPClient = class_getInstanceMethod(SKIOTCPClientClass, debugSendDataContent); Method originalMethod = class_getInstanceMethod(SKIOTCPClientClass, sendDataContent); // 交换SKIOTCPClient里的两个方法 method_exchangeImplementations(originalMethod, debugSendDataContentMethod_SKIOTCPClient); NSLog(@"exchange success"); }else{ NSLog(@"exchange failure, method already exist"); }}- (void)debugSendDataContent:(NSObject *)content writeBlock:(void(^)(NSInteger errorCode))writeBlock{ [self debugSendDataContent:content writeBlock:writeBlock]; // ...}@end
调用class_addMethod
给需要hook的类手动添加你需要exchange的方法,就相当于实现了category,需要关注的是,添加了你自己的方法后,需要再通过class_getInstanceMethod
将你添加的方法从被hook的类里拿出来,再执行method_exchangeImplementations
抽取出来
+ (void)hookClass:(Class)originalClass originalSelector:(SEL)originalSelector toClass:(Class)swizzleClass swizzleSelector:(SEL)swizzleSelector{ Method swizzleMethod = class_getInstanceMethod(swizzleClass, swizzleSelector); IMP swizzleImp = method_getImplementation(swizzleMethod); char *swizzleTypes = (char *)method_getTypeEncoding(swizzleMethod); // 将要exchange的方法添加到原始类中 BOOL success = class_addMethod(originalClass, swizzleSelector, swizzleImp, swizzleTypes); if (success) { // 从原始类中重新读取出添加的方法 Method swizzleMethod_original = class_getInstanceMethod(originalClass, swizzleSelector); Method originalMethod = class_getInstanceMethod(originalClass, originalSelector); // 交换原始类中两个方法实现 method_exchangeImplementations(originalMethod, swizzleMethod_original); NSLog(@"exchange success"); }else{ NSLog(@"exchange failure, method already exist"); }}