美文网首页iOS进阶指南
RSSwizzle源码解析

RSSwizzle源码解析

作者: 初心丶可曾记 | 来源:发表于2018-03-28 17:33 被阅读87次

    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对于宏定义的使用也是一大亮点。

    相关文章

      网友评论

        本文标题:RSSwizzle源码解析

        本文链接:https://www.haomeiwen.com/subject/goducftx.html