Aspects 源碼解析

簡介

Aspects是一個(gè)輕量的面向切面編程(AOP)的第三方庫,面向切面編程簡單來說蚀腿,就是在原來的業(yè)務(wù)流程之中的某個(gè)位置插入額外的功能慢味,不影響之前的邏輯澈蚌。舉個(gè)栗子來說亦镶,比如項(xiàng)目的統(tǒng)計(jì)埋點(diǎn)日月,統(tǒng)計(jì)頁面進(jìn)入的次數(shù)袱瓮,我們一般是hook@selector(viewWillAppear:)方法,然后加入埋點(diǎn)上報(bào)爱咬,這就相當(dāng)于我們在viewWillAppear中插入了一個(gè)切面來附加額外的功能尺借。

Aspects

先來看下頭文件,頭文件中只有兩個(gè)方法

+ (id<AspectToken>)aspect_hookSelector:(SEL)selector
                       withOptions:(AspectOptions)options
                        usingBlock:(id)block
                             error:(NSError **)error;
- (id<AspectToken>)aspect_hookSelector:(SEL)selector
                           withOptions:(AspectOptions)options
                            usingBlock:(id)block
                                 error:(NSError **)error;

+方法表示hook對類的所有實(shí)例對象生效(注意不是hook類方法)台颠,-方法表示對類的單個(gè)實(shí)例對象生效褐望。

進(jìn)入.m文件查看實(shí)現(xiàn),兩個(gè)方法都是調(diào)用了同一個(gè)c函數(shù)aspect_add();

static id aspect_add(id self, SEL selector, AspectOptions options, id block, NSError **error) {
NSCParameterAssert(self);
NSCParameterAssert(selector);
NSCParameterAssert(block);

__block AspectIdentifier *identifier = nil;
aspect_performLocked(^{
        if (aspect_isSelectorAllowedAndTrack(self, selector, options, error)) {
            AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);
            identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error];
            if (identifier) {
                [aspectContainer addAspect:identifier withOptions:options];
                
                aspect_prepareClassAndHookSelector(self, selector, error);
            }
        }
    });
    return identifier;
}   

aspect_performLocked函數(shù)是給block函數(shù)的執(zhí)行進(jìn)行加鎖處理串前,aspect_isSelectorAllowedAndTrack()判斷是否允許hook該方法瘫里。

aspect_isSelectorAllowedAndTrack

static BOOL aspect_isSelectorAllowedAndTrack(NSObject *self, SEL selector, AspectOptions options, NSError **error) {
    static NSSet *disallowedSelectorList;
    static dispatch_once_t pred;
    dispatch_once(&pred, ^{
        //設(shè)置hook黑名單
        disallowedSelectorList = [NSSet setWithObjects:@"retain", @"release", @"autorelease", @"forwardInvocation:", nil];
    });

    //檢查黑名單,retain荡碾,release谨读,autorelease,forwardInvocation方法不允許hook
    NSString *selectorName = NSStringFromSelector(selector);
    if ([disallowedSelectorList containsObject:selectorName]) {
        NSString *errorDescription = [NSString stringWithFormat:@"Selector %@ is blacklisted.", selectorName];
        AspectError(AspectErrorSelectorBlacklisted, errorDescription);
        return NO;
    }

    //針對dealloc方法的hook有額外要求坛吁,插入的block代碼只能選擇在dealloc之前執(zhí)行
    AspectOptions position = options&AspectPositionFilter;
    if ([selectorName isEqualToString:@"dealloc"] && position != AspectPositionBefore) {
        NSString *errorDesc = @"AspectPositionBefore is the only valid position when hooking dealloc.";
        AspectError(AspectErrorSelectorDeallocPosition, errorDesc);
        return NO;
    }
    //從![self.class instancesRespondToSelector:selector]可以看出是不能hook類方法的
    if (![self respondsToSelector:selector] && ![self.class instancesRespondToSelector:selector]) {
        NSString *errorDesc = [NSString stringWithFormat:@"Unable to find selector -[%@ %@].", NSStringFromClass(self.class), selectorName];
        AspectError(AspectErrorDoesNotRespondToSelector, errorDesc);
        return NO;
    }

    //判斷是否是元類劳殖,也即是判斷self是類還是對象。若self是類(如UIViewController)拨脉,進(jìn)入if分支哆姻;若self是對象,則直接renturn YES玫膀。
    if (class_isMetaClass(object_getClass(self))) {
        Class klass = [self class];
        //全局字典矛缨,Class為key,value是AspectTracker對象
        NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict();
        Class currentClass = [self class];

        AspectTracker *tracker = swizzledClassesDict[currentClass];
        //判斷子類是否已經(jīng)hook了該方法帖旨,整個(gè)繼承鏈上同一個(gè)方法只能hook一次
        if ([tracker subclassHasHookedSelectorName:selectorName]) {
            NSSet *subclassTracker = [tracker subclassTrackersHookingSelectorName:selectorName];
            NSSet *subclassNames = [subclassTracker valueForKey:@"trackedClassName"];
            NSString *errorDescription = [NSString stringWithFormat:@"Error: %@ already hooked subclasses: %@. A method can only be hooked once per class hierarchy.", selectorName, subclassNames];
            AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription);
            return NO;
        }
        
        //這一步do-while作用與上面的if相同箕昭,都是為了保證一個(gè)繼承鏈上只能有一次hook,但是兩個(gè)的場景不同解阅。上面的if判斷針對的場景是:先hook子類的方法落竹,再hook父類的同一方法,會從上面的if剔除父類的hook货抄。do-while的場景是:先hook父類的方法述召,再hook子類的方法,會從這一步剔除子類的hook碉熄。注意:針對同一個(gè)類的方法hook多次是可以的
        do {
            tracker = swizzledClassesDict[currentClass];
            if ([tracker.selectorNames containsObject:selectorName]) {
                if (klass == currentClass) {
                    return YES;//可以hook同一個(gè)類的方法多次
                }
                NSString *errorDescription = [NSString stringWithFormat:@"Error: %@ already hooked in %@. A method can only be hooked once per class hierarchy.", selectorName, NSStringFromClass(currentClass)];
                AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription);
                return NO;
            }
        } while ((currentClass = class_getSuperclass(currentClass)));

        currentClass = klass;
        AspectTracker *subclassTracker = nil;
        //這一步的do-while是保存selector并且遍歷父類桨武,將所有的類都進(jìn)行hook標(biāo)記,保證整個(gè)繼承鏈針對一個(gè)方法只能hook一次
        do {
            tracker = swizzledClassesDict[currentClass];
            if (!tracker) {
                tracker = [[AspectTracker alloc] initWithTrackedClass:currentClass];
                swizzledClassesDict[(id<NSCopying>)currentClass] = tracker;
            }
            if (subclassTracker) {
                [tracker addSubclassTracker:subclassTracker hookingSelectorName:selectorName];
            } else {
                [tracker.selectorNames addObject:selectorName];
            }
            subclassTracker = tracker;
        }while ((currentClass = class_getSuperclass(currentClass)));
    } else {
        //這里是對某個(gè)對象的hook锈津,直接return YES
        return YES;
    }

    return YES;
}

額外補(bǔ)充一點(diǎn)呀酸,區(qū)分[self class]和object_getClass(self)的不同。如果self是對象琼梆,則這兩個(gè)返回值一樣性誉,即self的對應(yīng)的類窿吩;如果self是類,[self class]返回的還是這個(gè)類本身也就是self,而object_getClass(self)返回的是它的元類错览。

回到aspect_add()函數(shù)纫雁,繼續(xù)往下看。

if (aspect_isSelectorAllowedAndTrack(self, selector, options, error)) {
        AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);
        identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error];
    if (identifier) {
        [aspectContainer addAspect:identifier withOptions:options];

        // Modify the class to allow message interception.
        aspect_prepareClassAndHookSelector(self, selector, error);
    }
}

AspectsContainer是一個(gè)容器類倾哺,AspectIdentifier類用來整合hook信息的轧邪,比如hook的selector,傳遞進(jìn)來的block,hook的對象等羞海,然后將identifier保存在aspectContainer中忌愚,根據(jù)我們傳遞的參數(shù)options,存放在內(nèi)部不同的數(shù)組中却邓。這里重點(diǎn)注意這個(gè)方法

+ (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError **)error {
    NSCParameterAssert(block);
    NSCParameterAssert(selector);
    NSMethodSignature *blockSignature = aspect_blockMethodSignature(block, error); //生成block的簽名
    if (!aspect_isCompatibleBlockSignature(blockSignature, object, selector, error)) {
        return nil;
    }

    AspectIdentifier *identifier = nil;
    if (blockSignature) {
        identifier = [AspectIdentifier new];
        identifier.selector = selector;
        identifier.block = block;
        identifier.blockSignature = blockSignature;
        identifier.options = options;
        identifier.object = object; // weak
    }
    return identifier;
}

aspect_blockMethodSignature()將我們傳遞進(jìn)來的block生成方法簽名硕糊。

aspect_blockMethodSignature

Aspects仿照OC自己定義了block,如下:

typedef struct _AspectBlock {
    __unused Class isa;
    AspectBlockFlags flags;
    __unused int reserved;
    void (__unused *invoke)(struct _AspectBlock *block, ...);
    struct {
        unsigned long int reserved;
        unsigned long int size;
        // requires AspectBlockFlagsHasCopyDisposeHelpers
        void (*copy)(void *dst, const void *src);
        void (*dispose)(const void *);
        // requires AspectBlockFlagsHasSignature
        const char *signature;
        const char *layout;
    } *descriptor;
    // imported variables
} *AspectBlockRef;

若想看系統(tǒng)中的block結(jié)構(gòu)可以通過clang編譯源文件腊徙,具體步驟可以看我之前的文章简十。可以看到結(jié)構(gòu)體中有個(gè)變量const char *signature撬腾,這個(gè)就是block的簽名串螟蝙。

static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error) {
    AspectBlockRef layout = (__bridge void *)block;
    if (!(layout->flags & AspectBlockFlagsHasSignature)) {
        NSString *description = [NSString stringWithFormat:@"The block %@ doesn't contain a type signature.", block];
        AspectError(AspectErrorMissingBlockSignature, description);
        return nil;
    }
    void *desc = layout->descriptor;
    desc += 2 * sizeof(unsigned long int);
    if (layout->flags & AspectBlockFlagsHasCopyDisposeHelpers) {
        desc += 2 * sizeof(void *);
    }
    if (!desc) {
        NSString *description = [NSString stringWithFormat:@"The block %@ doesn't has a type signature.", block];
        AspectError(AspectErrorMissingBlockSignature, description);
        return nil;
    }
    const char *signature = (*(const char **)desc);
    return [NSMethodSignature signatureWithObjCTypes:signature];
}

上面的代碼主要就是拿到signature,主要思路是從block中的descriptor地址偏移字節(jié)取到signature民傻。
生成block簽名之后還要檢測簽名的完整性胶逢,主要是檢測block的返回值,參數(shù)跟hook的方法的返回值和參數(shù)保持一致饰潜。

static BOOL aspect_isCompatibleBlockSignature(NSMethodSignature *blockSignature, id object, SEL selector, NSError **error) {
    NSCParameterAssert(blockSignature);
    NSCParameterAssert(object);
    NSCParameterAssert(selector);

    BOOL signaturesMatch = YES;
    //對入?yún)⒌膕elector生成方法簽名
    NSMethodSignature *methodSignature = [[object class] instanceMethodSignatureForSelector:selector];
    
    //判斷block和selector的參數(shù),block的參數(shù)個(gè)數(shù)可以小于等于selector的參數(shù)個(gè)數(shù)
    if (blockSignature.numberOfArguments > methodSignature.numberOfArguments) {
        signaturesMatch = NO;
    }else {
        if (blockSignature.numberOfArguments > 1) {
            const char *blockType = [blockSignature getArgumentTypeAtIndex:1];
            if (blockType[0] != '@') {
                signaturesMatch = NO;
            }
        }
        //從第三個(gè)參數(shù)開始比對block和selector的參數(shù)是否一致
        if (signaturesMatch) {
            for (NSUInteger idx = 2; idx < blockSignature.numberOfArguments; idx++) {
                const char *methodType = [methodSignature getArgumentTypeAtIndex:idx];
                const char *blockType = [blockSignature getArgumentTypeAtIndex:idx];
                // Only compare parameter, not the optional type data.
                if (!methodType || !blockType || methodType[0] != blockType[0]) {
                    signaturesMatch = NO; break;
                }
            }
        }
    }

    if (!signaturesMatch) {
        NSString *description = [NSString stringWithFormat:@"Block signature %@ doesn't match %@.", blockSignature, methodSignature];
        AspectError(AspectErrorIncompatibleBlockSignature, description);
        return NO;
    }
    return YES;
}

oc中的方法調(diào)用底層都是通過objc_msgSend()方法調(diào)用和簸,這個(gè)方法默認(rèn)傳遞兩個(gè)參數(shù),一個(gè)是調(diào)用的對象object(self)彭雾,還有一個(gè)是@selector,任何一個(gè)OC的方法都有這兩個(gè)參數(shù)锁保,所以block和selector的比較從index = 2的參數(shù)開始薯酝。比較參數(shù)比較的是參數(shù)的類型,如int類型就是i,SEL類型是:,OC對象是@,block是@?,更多的類型可以看蘋果官方文檔中關(guān)于typeEncoding的介紹爽柒。另外關(guān)于block的簽名只有一個(gè)默認(rèn)參數(shù)吴菠,第一個(gè)參數(shù)(index=0)是block本身,第二個(gè)參數(shù)Aspects約定是id<AspectInfo>類型的參數(shù)(為了和selector的簽名參數(shù)保持一致)浩村,所以上面判斷了block中第二個(gè)參數(shù)是否是id類型做葵。

接下來aspect_prepareClassAndHookSelector()方法就是Aspects核心代碼了。

核心代碼aspect_prepareClassAndHookSelector

static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError **error) {
    NSCParameterAssert(selector);
    Class klass = aspect_hookClass(self, error);
    Method targetMethod = class_getInstanceMethod(klass, selector);
    IMP targetMethodIMP = method_getImplementation(targetMethod);
    if (!aspect_isMsgForwardIMP(targetMethodIMP)) {
        // Make a method alias for the existing method implementation, it not already copied.
        const char *typeEncoding = method_getTypeEncoding(targetMethod);
        SEL aliasSelector = aspect_aliasForSelector(selector);
        if (![klass instancesRespondToSelector:aliasSelector]) {
            __unused BOOL addedAlias = class_addMethod(klass, aliasSelector, method_getImplementation(targetMethod), typeEncoding);
            NSCAssert(addedAlias, @"Original implementation for %@ is already copied to %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass);
        }

        // We use forwardInvocation to hook in.
        class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);
        AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
    }
}

aspect_hookClass()函數(shù)返回要hook類心墅,針對類和對象的hook返回值不同酿矢。

static Class aspect_hookClass(NSObject *self, NSError **error) {
    NSCParameterAssert(self);
    Class statedClass = self.class;
    Class baseClass = object_getClass(self);
    NSString *className = NSStringFromClass(baseClass);

    //判斷類是否是_Aspects_前綴開頭榨乎,對單個(gè)實(shí)例對象hook,會生成以_Aspects_為前綴中間類
    if ([className hasSuffix:AspectsSubclassSuffix]) {
        return baseClass;

    //表明這里是hook類而不是單個(gè)對象
    }else if (class_isMetaClass(baseClass)) {
        return aspect_swizzleClassInPlace((Class)self);
    
    //[self classs]和object_getClass(self)不相等瘫筐,表明可能是已經(jīng)KVO過的實(shí)例對象蜜暑,KVO的實(shí)現(xiàn)也是生成一個(gè)中間類,改變對象的isa指針指向這個(gè)中間類
    }else if (statedClass != baseClass) {
        return aspect_swizzleClassInPlace(baseClass);
    }

    //進(jìn)入這里表明self是一個(gè)實(shí)例對象策肝,并沒之前沒有hook任何方法
    //生成一個(gè)以_Aspects_前綴的類名
    const char *subclassName = [className stringByAppendingString:AspectsSubclassSuffix].UTF8String;
    //獲取類對象肛捍,如果為空,則通過runtime動(dòng)態(tài)生成子類繼承
    Class subclass = objc_getClass(subclassName);

    if (subclass == nil) {
        //runtime創(chuàng)建子類
        subclass = objc_allocateClassPair(baseClass, subclassName, 0);
        if (subclass == nil) {
            NSString *errrorDesc = [NSString stringWithFormat:@"objc_allocateClassPair failed to allocate class %s.", subclassName];
            AspectError(AspectErrorFailedToAllocateClassPair, errrorDesc);
            return nil;
        }
        
        aspect_swizzleForwardInvocation(subclass);
        //更改_Aspects_子類的- (Class)class;方法的返回值之众,返回的是原來的類(即_Aspects_的父類)
        aspect_hookedGetClass(subclass, statedClass);
        //同上拙毫,更改_Aspects_子類的元類為原來的類的元類
        aspect_hookedGetClass(object_getClass(subclass), statedClass);
        objc_registerClassPair(subclass);
    }
    //將self的isa指針指向_Aspects_開頭的子類
    object_setClass(self, subclass);
    return subclass;
}

從上可以看到,如果是hook類酝枢,直接返回這個(gè)類恬偷;如果是hook對象,會動(dòng)態(tài)生成_Aspects_前綴的中間類并將對象的isa設(shè)置為這個(gè)中間類(類似KVO)帘睦,如果對象是已經(jīng)KVO過的袍患,則直接返回KVO生成的中間類。拿到要hook的類之后竣付,就要將原始方法IMP實(shí)現(xiàn)進(jìn)行替換了诡延,替換是在aspect_swizzleForwardInvocation()函數(shù)進(jìn)行。

static void aspect_swizzleForwardInvocation(Class klass) {
    NSCParameterAssert(klass);

    IMP originalImplementation = class_replaceMethod(klass, @selector(forwardInvocation:), (IMP)__ASPECTS_ARE_BEING_CALLED__, "v@:@");
    if (originalImplementation) {
        class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, "v@:@");
    }
    AspectLog(@"Aspects: %@ is now aspect aware.", NSStringFromClass(klass));
}

class_replaceMethod()將forwardInvocation的實(shí)現(xiàn)替換成__ASPECTS_ARE_BEING_CALLED__()函數(shù)的實(shí)現(xiàn)古胆,如果替換成功(說明子類重寫了該方法)會返回替換之前的IMP肆良,然后為該類添加一個(gè)@selector(__aspects_forwardInvocation:)方法。我們知道forwardInvocation方法是runtime消息轉(zhuǎn)發(fā)機(jī)制中的最后一步轉(zhuǎn)發(fā)逸绎,Aspects是如何將消息轉(zhuǎn)發(fā)到這的呢惹恃?繼續(xù)往下看就知道了。
回到aspect_prepareClassAndHookSelector()函數(shù)棺牧,接來下通過aspect_isMsgForwardIMP()判斷原始selector的IMP是否是_objc_msgForward或者_(dá)objc_msgForward_stret巫糙,如果不是就將selector的IMP替換成_objc_msgForward或者_(dá)objc_msgForward_stret。另外通過runtime添加一個(gè)了aspects_為前綴的方法颊乘,IMP為原始selector的IMP参淹。對于_objc_msgForward()這個(gè)函數(shù),它是直接觸發(fā)消息轉(zhuǎn)發(fā)的乏悄,這里就對應(yīng)了前面的替換@selector(forwardInvocation:)IMP的用意浙值。當(dāng)我們給hook的selector發(fā)送消息時(shí),先執(zhí)行_objc_msgForward函數(shù)檩小,然后觸發(fā)消息轉(zhuǎn)發(fā)來到forwardInvocation:开呐。

為什么這里分為_objc_msgForward或者_(dá)objc_msgForward_stret,原因是函數(shù)的返回值是存放在寄存器中,寄存器的大小有限负蚊,當(dāng)返回值是某些很大的結(jié)構(gòu)體的時(shí)候神妹,寄存器存放不下,就會改成另外一種方式家妆。申請一段內(nèi)存鸵荠,把指針保存在寄存器上,返回值往這個(gè)指針指向的內(nèi)存寫數(shù)據(jù)伤极,這時(shí)就是用_objc_msgForward_stret方法了蛹找。

最后,我們只剩下__ASPECTS_ARE_BEING_CALLED__()函數(shù)沒有分析了哨坪。在前面的操作中有將forwardInvocation方法的IMP替換成這個(gè)函數(shù)庸疾,所以hook之后的消息最終會轉(zhuǎn)發(fā)到這個(gè)函數(shù)里面執(zhí)行,下面看看這個(gè)函數(shù)做了哪些事情当编。

__ASPECTS_ARE_BEING_CALLED__

static void __ASPECTS_ARE_BEING_CALLED__(__unsafe_unretained NSObject *self, SEL selector, NSInvocation *invocation) {
    NSCParameterAssert(self);
    NSCParameterAssert(invocation);
    //原始selector
    SEL originalSelector = invocation.selector;
    //aspects_前綴的selector
    SEL aliasSelector = aspect_aliasForSelector(invocation.selector);
    //替換為aspects_前綴的selector届慈,alisaSelector的IMP是原始selector的IMP,也就是表明[invocation invoke]時(shí)調(diào)用了原始方法
    invocation.selector = aliasSelector;
    AspectsContainer *objectContainer = objc_getAssociatedObject(self, aliasSelector);
    AspectsContainer *classContainer = aspect_getContainerForClass(object_getClass(self), aliasSelector);
    AspectInfo *info = [[AspectInfo alloc] initWithInstance:self invocation:invocation];
    NSArray *aspectsToRemove = nil;

    //這里執(zhí)行我們?nèi)雲(yún)r(shí)傳遞的block忿偷,對應(yīng)的option是AspectPositionBefore金顿,表明block執(zhí)行時(shí)機(jī)在原始方法之前
    aspect_invoke(classContainer.beforeAspects, info);
    aspect_invoke(objectContainer.beforeAspects, info);

    //這里對應(yīng)的option是AspectPositionInstead,表明用傳遞的block替換原來的方法
    BOOL respondsToAlias = YES;
    if (objectContainer.insteadAspects.count || classContainer.insteadAspects.count) {
        //這里表明替換原始方法
        aspect_invoke(classContainer.insteadAspects, info);
        aspect_invoke(objectContainer.insteadAspects, info);
    }else {
        Class klass = object_getClass(invocation.target);
        do {
            if ((respondsToAlias = [klass instancesRespondToSelector:aliasSelector])) {
                //這里表明執(zhí)行原始方法
                [invocation invoke];
                break;
            }
        }while (!respondsToAlias && (klass = class_getSuperclass(klass)));
    }

    //這里對應(yīng)的option是AspectPositionAfter鲤桥,表明block的執(zhí)行時(shí)機(jī)在原始方法之后
    aspect_invoke(classContainer.afterAspects, info);
    aspect_invoke(objectContainer.afterAspects, info);


    //如果該方法沒有被hook揍拆,直接執(zhí)行原始方法
    if (!respondsToAlias) {
        invocation.selector = originalSelector;
        SEL originalForwardInvocationSEL = NSSelectorFromString(AspectsForwardInvocationSelectorName);
        if ([self respondsToSelector:originalForwardInvocationSEL]) {
            ((void( *)(id, SEL, NSInvocation *))objc_msgSend)(self, originalForwardInvocationSEL, invocation);
        }else {
            [self doesNotRecognizeSelector:invocation.selector];
        }
    }

    [aspectsToRemove makeObjectsPerformSelector:@selector(remove)];
}

主要根據(jù)入?yún)r(shí)的option來插入block的執(zhí)行時(shí)機(jī),至此Aspects實(shí)現(xiàn)思路大致已經(jīng)解析完畢茶凳。最后為了有個(gè)更清晰的理解嫂拴,來個(gè)簡單的總結(jié)。

總結(jié)

針對單個(gè)實(shí)例對象hook(.h文件中的減方法)贮喧,生成一個(gè)動(dòng)態(tài)子類(_Aspects_為后綴),以類似kvo的方式生成一個(gè)中間類筒狠, 設(shè)置hook對象的isa為這個(gè)中間類,并且重寫中間類的class方法箱沦,返回原始類窟蓝。然后對于要hook的@selector,更改其IMP為_objc
_Msgforward / _objc_Msgforward_stret,某些情況當(dāng)@selector的返回值是結(jié)構(gòu)體的時(shí)候通過_objc_Msgforward_stret來觸發(fā)消息轉(zhuǎn)發(fā)饱普。更改中間類的@selector(forwardInvocation:)的IMP為__ASPECTS_ARE_BEING_CALLED__,給中間類添加一個(gè)以aspects__為前綴的方法,IMP為原始@selector的IMP状共。

針對類的所有對象都生效的hook(.h中的加方法),是直接對該類進(jìn)行處理套耕,不會生成中間類,處理過程同上峡继。

消息觸發(fā)的流程為:原始@selector -> _objc
_Msgforward / _objc_Msgforward_stret -> 觸發(fā)消息轉(zhuǎn)發(fā)@selector(forwardInvocation:) -> __ASPECTS_ARE_BEING_CALLED__(函數(shù)內(nèi)部執(zhí)行以aspects__為前綴的方法冯袍,也就是原始的@selecotr的IMP,根據(jù)傳入的flag參數(shù),確定插入的代碼執(zhí)行的時(shí)機(jī))

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市康愤,隨后出現(xiàn)的幾起案子儡循,更是在濱河造成了極大的恐慌,老刑警劉巖征冷,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锭弊,死亡現(xiàn)場離奇詭異鹃唯,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門便脊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人尝江,你說我怎么就攤上這事吠昭。” “怎么了饺律?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵窃页,是天一觀的道長。 經(jīng)常有香客問我复濒,道長脖卖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任芝薇,我火速辦了婚禮胚嘲,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘洛二。我一直安慰自己馋劈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布晾嘶。 她就那樣靜靜地躺著妓雾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪垒迂。 梳的紋絲不亂的頭發(fā)上械姻,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機(jī)與錄音机断,去河邊找鬼楷拳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛吏奸,可吹牛的內(nèi)容都是我干的欢揖。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼奋蔚,長吁一口氣:“原來是場噩夢啊……” “哼她混!你這毒婦竟也來了烈钞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤坤按,失蹤者是張志新(化名)和其女友劉穎毯欣,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體臭脓,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡酗钞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谢鹊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片算吩。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖佃扼,靈堂內(nèi)的尸體忽然破棺而出偎巢,到底是詐尸還是另有隱情,我是刑警寧澤兼耀,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布压昼,位于F島的核電站,受9級特大地震影響瘤运,放射性物質(zhì)發(fā)生泄漏窍霞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一拯坟、第九天 我趴在偏房一處隱蔽的房頂上張望但金。 院中可真熱鬧,春花似錦郁季、人聲如沸冷溃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽似枕。三九已至,卻和暖如春年柠,著一層夾襖步出監(jiān)牢的瞬間凿歼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工冗恨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留答憔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓掀抹,卻偏偏與公主長得像攀唯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子渴丸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容