目录
一个最简单的 CaptainHook 代码
在 .m
文件中单纯地导入 CaptainHook.h
头文件:
#import "CaptainHook.h"
预处理后的结果为:
// 这里对应 CaptainHook.h 开头的:
// #import <objc/runtime.h>
// #import <objc/message.h>
// #import <Foundation/NSObject.h>
// #import <Foundation/NSObjCRuntime.h>
#pragma clang module import ObjectiveC.runtime /* clang -E: implicit import for #import <objc/runtime.h> */
#pragma clang module import ObjectiveC.message /* clang -E: implicit import for #import <objc/message.h> */
#pragma clang module import Foundation.NSObject /* clang -E: implicit import for #import <Foundation/NSObject.h> */
#pragma clang module import Foundation.NSObjCRuntime /* clang -E: implicit import for #import <Foundation/NSObjCRuntime.h> */
// 定义一个"类声明结构体",用于存储一个类的:类对象、元类对象、父类对象。以允许在 Hook 该类的方法时,快速地查找该类的:类对象、元类对象、父类对象
struct CHClassDeclaration_
{
Class class_; // 用于保存类对象
Class metaClass_; // 用于保存元类对象
Class superClass_; // 用于保存父类对象
};
// 为 "类声明结构体" 取一个简单的别名
typedef struct CHClassDeclaration_ CHClassDeclaration_;
// 通过给定的类对象(value),初始化一个 "类声明结构体"(declaration)
static inline Class CHLoadClass_(CHClassDeclaration_* declaration, Class value)
{
declaration->class_ = value; // 设置类对象
declaration->metaClass_ = object_getClass(value); // 设置元类对象
declaration->superClass_ = class_getSuperclass(value); // 设置父类对象
return value; // 返回类对象本身
}
// 获取给定实例对象(object)中,给定名称(name)所标识的成员变量的值
__attribute__((unused)) inline __attribute__((always_inline))
static void* CHIvar_(id object, const char * name)
{
// 从实例对象(object)所属的类对象中,获取给定字符串(name)所标识的成员变量的结构体(ivar)
Ivar ivar = class_getInstanceVariable(object_getClass(object), name);
// 如果存在该成员变量,则通过实例对象(object)的基地址 + 成员变量相对于实例对象(object)基地址的偏移量,获取指向该成员变量的值的指针
// 如果不存在该成员变量,则返回空(nil 或者 0)
if (ivar)
return (void *)&((char *)(__bridge void *)object)[ivar_getOffset(ivar)];
return ((void*)0);
}
生成构造函数(CHConstructor)
在 CaptainHook.h
中,生成(构造函数)的宏接口为:
// 注意:
// 这里通过宏接口 CHConcat 将(CHConstructor)和(该宏接口在源文件中的行号)拼接成(构造函数的名称)
// 以防止在源文件中多次调用该宏,造成构造函数命名重复
#define CHConstructor static __attribute__((constructor)) void CHConcat(CHConstructor, __LINE__)()
如下代码:
#import "CaptainHook.h"
CHConstructor
{
NSLog(@"这是定义在第 3 行的构造函数");
}
CHConstructor
{
NSLog(@"这是定义在第 8 行的构造函数");
}
预处理后的结果为:
... ...
static __attribute__((constructor)) void CHConstructor3()
{
NSLog(@"这是定义在第 3 行的构造函数");
}
static __attribute__((constructor)) void CHConstructor8()
{
NSLog(@"这是定义在第 8 行的构造函数");
}
生成类的声明(CHDeclareClass)
在 CaptainHook.h
中,生成(类的声明)的宏接口为:
// @param.name 类名标记(直接写类名,不需要加 "" 或者 @"")
#define CHDeclareClass(name) \
@class name; \
static CHClassDeclaration_ name ## $;
该宏接口做了两件事:
- 通过
@class
关键字声明了一个name
类型的类 - 定义了一个
CHClassDeclaration_
结构体类型的静态全局变量name$
,用于在加载类时,存储类的:类对象、元类对象、父类对象
如下代码:
#import "CaptainHook.h"
CHDeclareClass(NSString)
CHDeclareClass(HcgPerson)
预处理后的结果为:
... ...
@class NSString;
static CHClassDeclaration_ NSString$;
@class HcgPerson;
static CHClassDeclaration_ HcgPerson$;
生成类的加载(CHLoadLateClass、CHLoadClass)
在 CaptainHook.h
中,生成(类的加载)的宏接口为:
// @param.name 类名标记(直接写类名,不需要加 "" 或者 @"")
#define CHLoadLateClass(name) CHLoadClass_(&name ## $, objc_getClass(#name))
#define CHLoadClass(name) CHLoadClass_(&name ## $, [name class])
宏接口 CHLoadLateClass
和宏接口 CHLoadClass
的底层都是通过调用静态函数 CHLoadClass_
来为相应的静态全局变量
static CHClassDeclaration_ name$
进行赋值:
static inline Class CHLoadClass_(CHClassDeclaration_* declaration, Class value)
{
declaration->class_ = value;
declaration->metaClass_ = object_getClass(value);
declaration->superClass_ = class_getSuperclass(value);
return value;
}
因为宏接口 CHLoadLateClass
在获取类对象时,是采用 objc_getClass(#name)
的方式
所以宏接口 CHLoadLateClass
既可以加载可链接的类,也可以加载不可链接的类
因为宏接口 CHLoadClass
在获取类对象时,是采用 [name class]
的方式
所以宏接口 CHLoadClass
只能加载可链接的类
建议:通常使用宏接口 CHLoadLateClass
来加载类
如下代码:
#import "CaptainHook.h"
#import "HcgPerson.h"
CHDeclareClass(HcgPerson)
CHConstructor
{
CHLoadClass(HcgPerson);
CHLoadLateClass(HcgPerson);
}
预处理后的结果为:
... ...
struct CHClassDeclaration_
{
Class class_;
Class metaClass_;
Class superClass_;
};
typedef struct CHClassDeclaration_ CHClassDeclaration_;
static inline Class CHLoadClass_(CHClassDeclaration_* declaration, Class value)
{
declaration->class_ = value;
declaration->metaClass_ = object_getClass(value);
declaration->superClass_ = class_getSuperclass(value);
return value;
}
... ...
@class HcgPerson;
static CHClassDeclaration_ HcgPerson$;
static __attribute__((constructor)) void CHConstructor6()
{
// 这里对应 CHLoadClass(HcgPerson);
CHLoadClass_(&HcgPerson$, [HcgPerson class]);
// 这里对应 CHLoadLateClass(HcgPerson);
CHLoadClass_(&HcgPerson$, objc_getClass("HcgPerson"));
}
其他常用的宏接口(CHClass、CHMetaClass、CHSuperClass、CHAlloc、CHSharedInstance、CHIsClass、CHRespondsTo)
在 CaptainHook.h
中,还提供了快速查找已加载的类,以及快速调用已加载的类上常用方法的宏接口:
// 从"类声明结构体"中获取缓存的类对象
// @param.name 类名标记(直接写类名,不需要加 "" 或者 @"")
#define CHClass(name) name ## $.class_
// 从"类声明结构体"中获取缓存的元类对象
// @param.name 类名标记(直接写类名,不需要加 "" 或者 @"")
#define CHMetaClass(name) name ## $.metaClass_
// 从"类声明结构体"中获取缓存的父类对象
// @param.name 类名标记(直接写类名,不需要加 "" 或者 @"")
#define CHSuperClass(name) name ## $.superClass_
// 调用给定类对象的 +alloc 方法
// @param.name 类名标记(直接写类名,不需要加 "" 或者 @"")
#define CHAlloc(name) ((name *)[CHClass(name) alloc])
// 调用给定类对象的 +sharedInstance 方法
// @param.name 类名标记(直接写类名,不需要加 "" 或者 @"")
#define CHSharedInstance(name) ((name *)[CHClass(name) sharedInstance])
// 调用给定实例对象或者给定类对象的 isKindOfClass: 方法
// @param.obj 实例对象 或者 类对象
// @param.name 类名标记(直接写类名,不需要加 "" 或者 @"")
#define CHIsClass(obj, name) [obj isKindOfClass:CHClass(name)]
// 调用给定实例对象或者给定类对象的 respondsToSelector: 方法
// @param.obj 实例对象 或者 类对象
// @param.sel 方法名标记(直接写方法名,不需要加 @selector())
#define CHRespondsTo(obj, sel) [obj respondsToSelector:@selector(sel)]
如下代码:
#import "HcgPerson.h"
#import "CaptainHook.h"
CHDeclareClass(HcgPerson)
CHConstructor
{
CHLoadLateClass(HcgPerson);
Class personClass = CHClass(HcgPerson);
Class personMetaClass = CHMetaClass(HcgPerson);
Class personSuperClass = CHSuperClass(HcgPerson);
HcgPerson* person0 = [CHAlloc(HcgPerson) init];
HcgPerson* person1 = CHSharedInstance(HcgPerson);
bool result0 = CHIsClass(person0, HcgPerson); // YES
bool result1 = CHIsClass(HcgPerson, HcgPerson); // NO
bool result2 = CHRespondsTo(person0, initWithName:age:); // YES
bool result3 = CHRespondsTo(HcgPerson, personWithName:age:); // YES
}
预处理后的结果为:
... ...
struct CHClassDeclaration_
{
Class class_;
Class metaClass_;
Class superClass_;
};
typedef struct CHClassDeclaration_ CHClassDeclaration_;
static inline Class CHLoadClass_(CHClassDeclaration_* declaration, Class value)
{
declaration->class_ = value;
declaration->metaClass_ = object_getClass(value);
declaration->superClass_ = class_getSuperclass(value);
return value;
}
... ...
@class HcgPerson;
static CHClassDeclaration_ HcgPerson$;
static __attribute__((constructor)) void CHConstructor6()
{
CHLoadClass_(&HcgPerson$, objc_getClass("HcgPerson"));
Class personClass = HcgPerson$.class_;
Class personMetaClass = HcgPerson$.metaClass_;
Class personSuperClass = HcgPerson$.superClass_;
HcgPerson* person0 = [((HcgPerson *)[HcgPerson$.class_ alloc]) init];
HcgPerson* person1 = ((HcgPerson *)[HcgPerson$.class_ sharedInstance]);
_Bool result0 = [person0 isKindOfClass:HcgPerson$.class_];
_Bool result1 = [HcgPerson isKindOfClass:HcgPerson$.class_];
_Bool result2 = [person0 respondsToSelector:@selector(initWithName:age:)];
_Bool result3 = [HcgPerson respondsToSelector:@selector(personWithName:age:)];
}