CaptainHook 源码分析(二):构造函数 && 类的声明 && 类的加载

一个最简单的 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 ## $;

该宏接口做了两件事:

  1. 通过 @class 关键字声明了一个 name 类型的类
  2. 定义了一个 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:)];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值