RSSwizzle源码解析
前言
RSSwizzle是一个轻量的线程安全的方法hook的库,平常我们仅仅通过runtime的method_exchangeImplementations函数来进行方法hook在特殊情况下会出现问题,而RSSwizzle就是在考虑到特殊情况下的一种完美解决方案。接下来,我们先从简单的用法上来深入它内部的细节。
RSSwizzle的使用
首先,我们先来看下用RSSwizzle怎么hook方法。
RSSwizzleInstanceMethod([UIViewController class], @selector(viewWillAppear:), RSSWReturnType(void), RSSWArguments(BOOL animated), RSSWReplacement({
RSSWCallOriginal(animated);
NSLog(@"view will appear");
}), 0, NULL);
注意这里的 RSSwizzleInstanceMethod
, RSSWReturnType
, RSSWArguments
, RSSWReplacement
, RSSWCallOriginal
都是定义好的宏。
上面的例子很简单,就是hook了UIViewController的viewWillAppear方法,并告诉了方法的返回类型是void,方法的参数列表为(BOOL animated),替换掉的方法在RSSWReplacement括起来的区域内,首先是通过RSSWCallOriginal(animated),调用原来的viewWillAppear方法,然后NSLog打印。
一眼看过来,全是宏,不容易看出具体的函数调用,下面我就会一步一步展开宏,看看完全展开后的宏的真面目。
宏定义的展开
首先第一个宏
#define RSSwizzleInstanceMethod(classToSwizzle, \
selector, \
RSSWReturnType, \
RSSWArguments, \
RSSWReplacement, \
RSSwizzleMode, \
key) \
_RSSwizzleInstanceMethod(classToSwizzle, \
selector, \
RSSWReturnType, \
_RSSWWrapArg(RSSWArguments), \
_RSSWWrapArg(RSSWReplacement), \
RSSwizzleMode, \
key)
将上面的例子代入展开后得到【展开式一】如下:
_RSSwizzleInstanceMethod([UIViewController class],
@selector(viewWillAppear:),
RSSWReturnType(void),
_RSSWWrapArg(RSSWArguments(BOOL animated)),
_RSSWWrapArg(RSSWReplacement({
RSSWCallOriginal(animated);
NSLog(@"view will appear");
})),
0,
NULL)
_RSSwizzleInstanceMethod宏的定义:
#define _RSSwizzleClassMethod(classToSwizzle, \
selector, \
RSSWReturnType, \
RSSWArguments, \
RSSWReplacement) \
[RSSwizzle \
swizzleClassMethod:selector \
inClass:[classToSwizzle class] \
newImpFactory:^id(RSSwizzleInfo *swizzleInfo) { \
RSSWReturnType (*originalImplementation_)(_RSSWDel3Arg(__unsafe_unretained id, \
SEL, \
RSSWArguments)); \
SEL selector_ = selector; \
return ^RSSWReturnType (_RSSWDel2Arg(__unsafe_unretained id self, \
RSSWArguments)) \
{ \
RSSWReplacement \
}; \
}];
在【展开式一】上接着展开宏得到【展开式二】:
[RSSwizzle
swizzleInstanceMethod:@selector(viewWillAppear:)
inClass:[[UIViewController class] class]
newImpFactory:^id(RSSwizzleInfo *swizzleInfo) {
RSSWReturnType(void) (*originalImplementation_)(_RSSWDel3Arg(__unsafe_unretained id,
SEL,
_RSSWWrapArg(RSSWArguments(BOOL animated))));
SEL selector_ = @selector(viewWillAppear:);
return ^RSSWReturnType(void) (_RSSWDel2Arg(__unsafe_unretained id self,
_RSSWWrapArg(RSSWArguments(BOOL animated))))
{
_RSSWWrapArg(RSSWReplacement({
RSSWCallOriginal(animated);
NSLog(@"view will appear");
}))
};
}
mode:0
key:NULL];
最后集中看一些小的宏定义:
#define RSSWReturnType(type) type
#define RSSWArguments(arguments...) _RSSWArguments(arguments)
#define _RSSWArguments(arguments...) DEL, ##arguments
#define RSSWReplacement(code...) code
#define _RSSWWrapArg(args...) args
#define _RSSWDel2Arg(a1, a2, args...) a1, ##args
#define _RSSWDel3Arg(a1, a2, a3, args...) a1, a2, ##args
#define RSSWCallOriginal(arguments...) _RSSWCallOriginal(arguments)
#define _RSSWCallOriginal(arguments...) \
((__typeof(originalImplementation_))[swizzleInfo \
getOriginalImplementation])(self, \
selector_, \
##arguments)
将【展开式二】中余下的一些宏展开:
1. RSSWReturnType(void) ===> void
2. RSSWArguments(BOOL animated) ===> _RSSWArguments(BOOL animated) ===> DEL, BOOL animated
_RSSWWrapArg(RSSWArguments(BOOL animated)) ===> DEL, BOOL animated
_RSSWDel2Arg(__unsafe_unretained id self,_RSSWWrapArg(RSSWArguments(BOOL animated))) ===> _RSSWDel2Arg(__unsafe_unretained id self,DEL, BOOL animated) ===> __unsafe_unretained id self, BOOL animated
_RSSWDel3Arg(__unsafe_unretained id,SEL,_RSSWWrapArg(RSSWArguments(BOOL animated))) ===> __unsafe_unretained id,SEL,BOOL animated
3. _RSSWWrapArg(RSSWReplacement({
RSSWCallOriginal(animated);
NSLog(@"view will appear");
}))
展开成
((__typeof(originalImplementation_))[swizzleInfo getOriginalImplementation])(self, selector_, animated);
NSLog(@"view will appear");
将上面的各项展开的结果代入【展开式二】得到最终展开的代码:
[RSSwizzle swizzleInstanceMethod:@selector(viewWillAppear:) inClass:[UIViewController class] newImpFactory:^id(RSSwizzleInfo *swizzleInfo) {
void (*originalImplementation_)( id,SEL,BOOL animated);
SEL selector_ = @selector(viewWillAppear:);
return ^void (__unsafe_unretained id self,BOOL animated)
{
((__typeof(originalImplementation_))[swizzleInfo
getOriginalImplementation])(self, selector_, animated);
NSLog(@"view will appear");
};
} mode:0 key:NULL];
通过最终的展开代码,可以清楚的看到是调用了RSSwizzle类中的@selector(swizzleInstanceMethod:inClass:newImpFactory:mode:key:)方法,接下来从这个方法入手,解析整个的hook过程。
源码解析
打开RSSwizzle.h文件,头文件只有两个方法:
+(BOOL)swizzleInstanceMethod:(SEL)selector
inClass:(Class)classToSwizzle
newImpFactory:(RSSwizzleImpFactoryBlock)factoryBlock
mode:(RSSwizzleMode)mode
key:(const void *)key;
+(void)swizzleClassMethod:(SEL)selector
inClass:(Class)classToSwizzle
newImpFactory:(RSSwizzleImpFactoryBlock)factoryBlock;
一个用来hook对象方法,一个用来hook类方法。我们知道,对象方法保存在类中,而类方法保存在元类中,所以swizzleClassMethod这个方法直接通过入参object_getClass(classToSwizzle)找到元类调用swizzleInstanceMethod方法,所以我们只需分析第一个方法即可。
+(BOOL)swizzleInstanceMethod:(SEL)selector
inClass:(Class)classToSwizzle
newImpFactory:(RSSwizzleImpFactoryBlock)factoryBlock
mode:(RSSwizzleMode)mode
key:(const void *)key
{
NSAssert(!(NULL == key && RSSwizzleModeAlways != mode),
@"Key may not be NULL if mode is not RSSwizzleModeAlways.");
@synchronized(swizzledClassesDictionary()){
if (key){
NSSet *swizzledClasses = swizzledClassesForKey(key);
if (mode == RSSwizzleModeOncePerClass) {
if ([swizzledClasses containsObject:classToSwizzle]){
return NO;
}
}else if (mode == RSSwizzleModeOncePerClassAndSuperclasses){
for (Class currentClass = classToSwizzle;
nil != currentClass;
currentClass = class_getSuperclass(currentClass))
{
if ([swizzledClasses containsObject:currentClass]) {
return NO;
}
}
}
}
swizzle(classToSwizzle, selector, factoryBlock);
if (key){
[swizzledClassesForKey(key) addObject:classToSwizzle];
}
}
return YES;
}
首先进行入参key和mode的参数检查,然后判断key是否存在,如果存在,根据mode判断该类或者整个继承链上是否之前已经hook了一次该方法。RSSwizzleModeOncePerClass表明这个类只允许一个方法hook一次,RSSwizzleModeOncePerClassAndSuperclasses则是在继承链上任意一个类中一个方法只允许hook一次(比如父类hook了,子类不能再hook)。然后调用swizzle()替换原始方法实现,这个就是hook的核心代码了。
static void swizzle(Class classToSwizzle,
SEL selector,
RSSwizzleImpFactoryBlock factoryBlock)
{
Method method = class_getInstanceMethod(classToSwizzle, selector);
NSCAssert(NULL != method,
@"Selector %@ not found in %@ methods of class %@.",
NSStringFromSelector(selector),
class_isMetaClass(classToSwizzle) ? @"class" : @"instance",
classToSwizzle);
NSCAssert(blockIsAnImpFactoryBlock(factoryBlock),
@"Wrong type of implementation factory block.");
__block OSSpinLock lock = OS_SPINLOCK_INIT;
__block IMP originalIMP = NULL;
RSSWizzleImpProvider originalImpProvider = ^IMP{
OSSpinLockLock(&lock);
IMP imp = originalIMP;
OSSpinLockUnlock(&lock);
if (NULL == imp){
Class superclass = class_getSuperclass(classToSwizzle);
imp = method_getImplementation(class_getInstanceMethod(superclass,selector));
}
return imp;
};
RSSwizzleInfo *swizzleInfo = [RSSwizzleInfo new];
swizzleInfo.selector = selector;
swizzleInfo.impProviderBlock = originalImpProvider;
id newIMPBlock = factoryBlock(swizzleInfo);
const char *methodType = method_getTypeEncoding(method);
NSCAssert(blockIsCompatibleWithMethodType(newIMPBlock,methodType),
@"Block returned from factory is not compatible with method type.");
IMP newIMP = imp_implementationWithBlock(newIMPBlock);
OSSpinLockLock(&lock);
originalIMP = class_replaceMethod(classToSwizzle, selector, newIMP, methodType);
OSSpinLockUnlock(&lock);
}
首先获取Method,如果classToSwizzle参数是类,则返回的对象方法;如果classToSwizzle是元类,则返回的类方法。然后通过blockIsAnImpFactoryBlock()函数判断入参的factoryBlock是否参数以及返回值相符的block,代码如下:
static BOOL blockIsAnImpFactoryBlock(id block){
const char *blockType = blockGetType(block);
RSSwizzleImpFactoryBlock dummyFactory = ^id(RSSwizzleInfo *swizzleInfo){
return nil;
};
const char *factoryType = blockGetType(dummyFactory);
return 0 == strcmp(factoryType, blockType);
}
blockGetType()用于返回block的签名,然后下面定义了一个规范的block并获取这个block的签名,然后比较两个签名是否一致,一致则表明入参时的block符合规范。
blockGetType函数如下:
static const char *blockGetType(id block){
struct Block_literal_1 *blockRef = (__bridge struct Block_literal_1 *)block;
BlockFlags flags = blockRef->flags;
if (flags & BLOCK_HAS_SIGNATURE) {
void *signatureLocation = blockRef->descriptor;
signatureLocation += sizeof(unsigned long int);
signatureLocation += sizeof(unsigned long int);
if (flags & BLOCK_HAS_COPY_DISPOSE) {
signatureLocation += sizeof(void(*)(void *dst, void *src));
signatureLocation += sizeof(void (*)(void *src));
}
const char *signature = (*(const char **)signatureLocation);
return signature;
}
return NULL;
}
RSSwizzle作者按照block的真实布局定义了一个struct Block_literal_1结构体
struct Block_literal_1 {
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor_1 {
unsigned long int reserved; // NULL
unsigned long int size; // sizeof(struct Block_literal_1)
// optional helper functions
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
void (*dispose_helper)(void *src); // IFF (1<<25)
// required ABI.2010.3.16
const char *signature; // IFF (1<<30)
} *descriptor;
// imported variables
};
block结构体内部有个signature成员变量,这个就是block的签名,注意不能直接通过blockRef->descriptor->signature获取签名,因为不同场景下的block结构有差别,比如当block内部引用了外面的局部变量,并且这个局部变量是OC对象,或者是__block关键词包装的变量,block的结构里面有copy和dispose函数,因为这两种变量都是属于内存管理的范畴的;其他场景下的block就未必有copy和dispose函数。所以可以看到blockGetType()内部是通过flag判断是否有签名,以及是否有copy和dispose函数,然后通过地址偏移找到signature。
回到swizzle()函数,接下来定义了一个originalImpProvider的block,待会再来看。往下接着创建了一个RSSwizzleInfo对象,将刚刚定义的block以及hook的selecor保存在该对象中,然后调用入参的factoryBlock(swizzleInfo)返回一个新的block,入参的factoryBlock对象如下:
id (factoryBlock)(RSSwizzleInfo *) = ^id(RSSwizzleInfo *swizzleInfo) {
void (*originalImplementation_)( id,SEL,BOOL animated);
SEL selector_ = @selector(viewWillAppear:);
return ^void (__unsafe_unretained id self,BOOL animated)
{
((__typeof(originalImplementation_))[swizzleInfo
getOriginalImplementation])(self, selector_, animated);
NSLog(@"view will appear");
}
}
所以返回的是一个
^void (__unsafe_unretained id self,BOOL animated)
{
((__typeof(originalImplementation_))[swizzleInfo
getOriginalImplementation])(self, selector_, animated);
NSLog(@"view will appear");
}
这样的block。runtime中,可以将一个selector的IMP等价替换成block的IMP,我们知道通过objc_msgSend()发送消息,前两个参数为,id self和SEL selector,第一个参数是表明对哪个对象发消息,第二个参数则是消息名称。转成等价的block则block的参数只需要第一个参数为id self,其余的参数与selector中对应即可。所以blockIsCompatibleWithMethodType()函数中就是判断block的签名是否与selector的签名匹配
static BOOL blockIsCompatibleWithMethodType(id block, const char *methodType){
const char *blockType = blockGetType(block);
NSMethodSignature *blockSignature;
if (0 == strncmp(blockType, (const char *)"@\"", 2)) {
// Block return type includes class name for id types
// while methodType does not include.
// Stripping out return class name.
char *quotePtr = strchr(blockType+2, '"');
if (NULL != quotePtr) {
++quotePtr;
char filteredType[strlen(quotePtr) + 2];
memset(filteredType, 0, sizeof(filteredType));
*filteredType = '@';
strncpy(filteredType + 1, quotePtr, sizeof(filteredType) - 2);
blockSignature = [NSMethodSignature signatureWithObjCTypes:filteredType];
}else{
return NO;
}
}else{
blockSignature = [NSMethodSignature signatureWithObjCTypes:blockType];
}
NSMethodSignature *methodSignature =
[NSMethodSignature signatureWithObjCTypes:methodType];
if (!blockSignature || !methodSignature) {
return NO;
}
if (blockSignature.numberOfArguments != methodSignature.numberOfArguments){
return NO;
}
if (strcmp(blockSignature.methodReturnType, methodSignature.methodReturnType) != 0) {
return NO;
}
for (int i=0; i<methodSignature.numberOfArguments; ++i){
if (i == 0){
// self in method, block in block
if (strcmp([methodSignature getArgumentTypeAtIndex:i], "@") != 0) {
return NO;
}
if (strcmp([blockSignature getArgumentTypeAtIndex:i], "@?") != 0) {
return NO;
}
}else if(i == 1){
// SEL in method, self in block
if (strcmp([methodSignature getArgumentTypeAtIndex:i], ":") != 0) {
return NO;
}
if (strncmp([blockSignature getArgumentTypeAtIndex:i], "@", 1) != 0) {
return NO;
}
}else {
const char *blockSignatureArg = [blockSignature getArgumentTypeAtIndex:i];
if (strncmp(blockSignatureArg, "@?", 2) == 0) {
// Handle function pointer / block arguments
blockSignatureArg = "@?";
}
else if (strncmp(blockSignatureArg, "@", 1) == 0) {
blockSignatureArg = "@";
}
if (strcmp(blockSignatureArg,
[methodSignature getArgumentTypeAtIndex:i]) != 0)
{
return NO;
}
}
}
return YES;
}
大致流程是:
1、判断block签名的第一个参数是否为 @?
(@?就表示block类型)
2、判断block签名第二个参数是否为@
(id 类型)
3、判断selctor中的余下参数是否跟block中参数类型相同。
校验完block后,通过imp_implementationWithBlock()转成IMP,然后用class_replaceMethod()将原始selector的IMP替换成这个block的IMP,如果该类中没有实现这个Selector方法,则返回值为nil;否则返回原始的IMP。最后来看originalImpProvider:
RSSWizzleImpProvider originalImpProvider = ^IMP{
OSSpinLockLock(&lock);
IMP imp = originalIMP;
OSSpinLockUnlock(&lock);
if (NULL == imp){
superclasses.
Class superclass = class_getSuperclass(classToSwizzle);
imp = method_getImplementation(class_getInstanceMethod(superclass,selector));
}
return imp;
};
如果originalIMP为空,也就是该类中没有实现selector方法,则沿着继承链找寻父类的实现;否则直接返回selector的原始实现。关于为什么要去动态查找父类实现,可以看我之前的博客。
RSSwizzle的主要思路是hook本类没有实现的方法(继承了父类的方法),不会去copy父类的实现并添加方法到本类中,而是在消息触发时动态去父类中查找以调用原来的实现。同时RSSwizzle对于宏定义的使用也是一大亮点。
网友评论