RSSwizzle源码解析

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);

注意这里的 RSSwizzleInstanceMethodRSSWReturnTypeRSSWArgumentsRSSWReplacementRSSWCallOriginal都是定义好的宏。

上面的例子很简单,就是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对于宏定义的使用也是一大亮点。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268