Runtime最佳實(shí)踐之Aspects

來(lái)之網(wǎng)絡(luò)

闖蕩開(kāi)發(fā)那些街
有誰(shuí)不識(shí)Runtime
但卻只聞黑魔法
從未項(xiàng)目運(yùn)乾坤

一脖母、前言

使用Objective-C開(kāi)發(fā)iOS項(xiàng)目拢军,一定聽(tīng)多了Runtime也用了很多采用黑魔法的第三方庫(kù)正蛙。比如

1.Aspects(AOP必備吟榴,“取締” baseVC猖吴,無(wú)侵入埋點(diǎn))
2.MJExtension(JSON 轉(zhuǎn) model憔恳,一行代碼實(shí)現(xiàn) NSCoding 協(xié)議的自動(dòng)歸檔和解檔)
3.JSPatch(動(dòng)態(tài)下發(fā) JS 進(jìn)行熱修復(fù))
4.NullSafe(防止因發(fā) unrecognised messages 給 NSNull 導(dǎo)致的崩潰)
5.UITableView-FDTemplateLayoutCell(自動(dòng)計(jì)算并緩存 table view 的 cell 高度)
6.UINavigationController+FDFullscreenPopGesture(全屏滑動(dòng)返回)

也一定知道runtime的消息轉(zhuǎn)發(fā)機(jī)制

1.Method resolution 方法解析處理階段:+(BOOL)resolveInstanceMethod:(SEL)sel 和 +(BOOL)resolveClassMethod:(SEL)sel瓤荔。 如果沒(méi)有找到,可以通過(guò)新添加一個(gè)方法并返回此方法類避免崩潰钥组。
2.Fast forwarding 快速轉(zhuǎn)發(fā)階段: -(id)forwardingTargetForSelector:(SEL)aSelector输硝。 如果沒(méi)有找到,則可以通過(guò)返回一個(gè)自定義的類程梦。
3.Normal forwarding 常規(guī)轉(zhuǎn)發(fā)階段:(1)-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 對(duì)象根據(jù)SEL自動(dòng)創(chuàng)建簽名 (2)-(void)forwardInvocation:(NSInvocation *)anInvocation 消息轉(zhuǎn)發(fā)点把。
但是僅僅知道了理論知識(shí),沒(méi)有實(shí)踐下屿附,那么體會(huì)不到黑魔法的真正作用郎逃。

二、AOP

當(dāng)聽(tīng)說(shuō)AOP的時(shí)候感覺(jué)還挺新鮮挺份,但是了解到實(shí)現(xiàn)的技術(shù)后褒翰,發(fā)現(xiàn)已經(jīng)是“老情人”了。突然讓我感覺(jué)到Runtime真的是可以做甚多事匀泊,很是激動(dòng)优训。也明白OC的高級(jí)能力需要通過(guò)C語(yǔ)言來(lái)加持,即C語(yǔ)言學(xué)的透那么OC學(xué)的就深各聘。(刷LeetCode用C語(yǔ)言吧)
興趣來(lái)了去看看Aspects的源碼吧型宙,將自己的不足處補(bǔ)補(bǔ),不然我的水桶盛水有點(diǎn)淺伦吠。??????

三妆兑、Aspects

Aspects雖然已經(jīng)5、6年沒(méi)有更新了毛仪,里面的代碼有一點(diǎn)快要腐敗的味道(如OSSpinLock已經(jīng)不是線程完全了)搁嗓,但是菜還是一道好菜,吃起來(lái)蠻香的箱靴。
寫(xiě)一個(gè)demo試試

#import <Aspects.h>

采用pod 'Aspects', '~> 1.4.1'集成后腺逛,引用頭文件。

[self aspect_hookSelector:@selector(justTest) withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> info) {
        NSLog(@"Hello World!");
    } error:nil];

將添加鉤子的方法添加到類中衡怀,需要在調(diào)用justTest方法之前棍矛。

- (void)justTest
{
    NSLog(@"wyn 222");
}

原來(lái)的justTest方法。
運(yùn)行后打印

MyDream[12617:504544] Hello World!
MyDream[12617:504544] wyn 222

通過(guò)AspectOptions還可以設(shè)置加載在原方法的前面抛杨,替換還是后面够委。

四、Aspects關(guān)系圖

看了源碼后整理下類的關(guān)系圖


Aspects類關(guān)系圖

采用的都是私有內(nèi)部類怖现。

五茁帽、 Aspects思路

1. 通過(guò)aspect_add方法作為入口玉罐。
2. 在線程安全中,判斷selector是否可以進(jìn)行鉤子潘拨。
3. 為一個(gè)selector方法新建一個(gè)容器AspectsContainer吊输。容器通過(guò)關(guān)聯(lián)對(duì)象綁定到self中。
4. 并且一個(gè)selector可以添加多次铁追,如在執(zhí)行前季蚂,替換和執(zhí)行后。
5. 創(chuàng)建切面識(shí)別AspectIdentifier,叢中包含了主入口的所有參數(shù)琅束。
6. 在容器中根據(jù)AspectOptions添加切面識(shí)別扭屁。
7. 然后像KVO一樣創(chuàng)建一個(gè)子類,替換_objc_msgForward__ASPECTS_ARE_BEING_CALLED__;
8. 移除切面的函數(shù)為AspectToken的- (BOOL)remove;狰闪。

六疯搅、 Aspects注釋

所有方法都加了注釋,可以通過(guò)注釋明白都是在處理什么埋泵。碰到不清楚用法的地方幔欧,可以問(wèn)問(wèn)度娘。
Aspects.h

#import <Foundation/Foundation.h>

// 用來(lái)表示加載在原方法的前面丽声,替換還是礁蔗,后面。
typedef NS_OPTIONS(NSUInteger, AspectOptions) {
    AspectPositionAfter   = 0,            /// Called after the original implementation (default)
    AspectPositionInstead = 1,            /// Will replace the original implementation.
    AspectPositionBefore  = 2,            /// Called before the original implementation.
    
    AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution.
};

// 添加一個(gè)鉤子方法時(shí)雁社,返回一個(gè)token對(duì)象浴井,用來(lái)刪除鉤子。
/// Opaque Aspect Token that allows to deregister the hook.
@protocol AspectToken <NSObject>

// 直接調(diào)用刪除就可以將鉤子方法從之前的注冊(cè)集合中刪除霉撵。
/// Deregisters an aspect.
/// @return YES if deregistration is successful, otherwise NO.
- (BOOL)remove;

@end

// 保存著注冊(cè)時(shí)傳入的參數(shù)內(nèi)容
/// The AspectInfo protocol is the first parameter of our block syntax.
@protocol AspectInfo <NSObject>

/// The instance that is currently hooked.
- (id)instance;

/// The original invocation of the hooked method.
- (NSInvocation *)originalInvocation;

/// All method arguments, boxed. This is lazily evaluated.
- (NSArray *)arguments;

@end

/**
 Aspects uses Objective-C message forwarding to hook into messages. This will create some overhead. Don't add aspects to methods that are called a lot. Aspects is meant for view/controller code that is not called a 1000 times per second.

 Adding aspects returns an opaque token which can be used to deregister again. All calls are thread safe.
 */
@interface NSObject (Aspects)

/// Adds a block of code before/instead/after the current `selector` for a specific class.
///
/// @param block Aspects replicates the type signature of the method being hooked.
/// The first parameter will be `id<AspectInfo>`, followed by all parameters of the method.
/// These parameters are optional and will be filled to match the block signature.
/// You can even use an empty block, or one that simple gets `id<AspectInfo>`.
///
/// @note Hooking static methods is not supported.
/// @return A token which allows to later deregister the aspect.
+ (id<AspectToken>)aspect_hookSelector:(SEL)selector
                      withOptions:(AspectOptions)options
                       usingBlock:(id)block
                            error:(NSError **)error;

/// Adds a block of code before/instead/after the current `selector` for a specific instance.
- (id<AspectToken>)aspect_hookSelector:(SEL)selector
                      withOptions:(AspectOptions)options
                       usingBlock:(id)block
                            error:(NSError **)error;

@end

// 錯(cuò)誤代碼
typedef NS_ENUM(NSUInteger, AspectErrorCode) {
    AspectErrorSelectorBlacklisted,                   /// Selectors like release, retain, autorelease are blacklisted.
    AspectErrorDoesNotRespondToSelector,              /// Selector could not be found.
    AspectErrorSelectorDeallocPosition,               /// When hooking dealloc, only AspectPositionBefore is allowed.
    AspectErrorSelectorAlreadyHookedInClassHierarchy, /// Statically hooking the same method in subclasses is not allowed.
    AspectErrorFailedToAllocateClassPair,             /// The runtime failed creating a class pair.
    AspectErrorMissingBlockSignature,                 /// The block misses compile time signature info and can't be called.
    AspectErrorIncompatibleBlockSignature,            /// The block signature does not match the method or is too large.

    AspectErrorRemoveObjectAlreadyDeallocated = 100   /// (for removing) The object hooked is already deallocated.
};

extern NSString *const AspectErrorDomain;

Aspects.m

#import "Aspects.h"
#import <libkern/OSAtomic.h>
#import <objc/runtime.h>
#import <objc/message.h>

#define AspectLog(...)
//#define AspectLog(...) do { NSLog(__VA_ARGS__); }while(0)
#define AspectLogError(...) do { NSLog(__VA_ARGS__); }while(0)

// Block internals.
typedef NS_OPTIONS(int, AspectBlockFlags) {
    AspectBlockFlagsHasCopyDisposeHelpers = (1 << 25),
    AspectBlockFlagsHasSignature          = (1 << 30)
};
// 作用是將傳入的OC類對(duì)象的block磺浙,轉(zhuǎn)化為C語(yǔ)言結(jié)構(gòu)體,方法獲取內(nèi)容來(lái)生成實(shí)例簽名徒坡。
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;

// 切面信息撕氧,保存了原來(lái)的實(shí)例,參數(shù)喇完,原來(lái)的調(diào)用伦泥。
// 在forwardInvocation的時(shí)候用到
@interface AspectInfo : NSObject <AspectInfo>
- (id)initWithInstance:(__unsafe_unretained id)instance invocation:(NSInvocation *)invocation;
@property (nonatomic, unsafe_unretained, readonly) id instance;
@property (nonatomic, strong, readonly) NSArray *arguments;
@property (nonatomic, strong, readonly) NSInvocation *originalInvocation;
@end

// 切面標(biāo)識(shí),將所有的入?yún)b成一個(gè)Identifier,并且添加了一個(gè)block的簽名锦溪。
// Tracks a single aspect.
@interface AspectIdentifier : NSObject
+ (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError **)error;
- (BOOL)invokeWithInfo:(id<AspectInfo>)info;
@property (nonatomic, assign) SEL selector;
@property (nonatomic, strong) id block;
@property (nonatomic, strong) NSMethodSignature *blockSignature;
@property (nonatomic, weak) id object;
@property (nonatomic, assign) AspectOptions options;
@end

// 一個(gè)selector對(duì)應(yīng)一個(gè)容器不脯。容器中主要用來(lái)保存切面的標(biāo)識(shí)。
// Tracks all aspects for an object/class.
@interface AspectsContainer : NSObject
- (void)addAspect:(AspectIdentifier *)aspect withOptions:(AspectOptions)injectPosition;
- (BOOL)removeAspect:(id)aspect;
- (BOOL)hasAspects;
@property (atomic, copy) NSArray *beforeAspects;
@property (atomic, copy) NSArray *insteadAspects;
@property (atomic, copy) NSArray *afterAspects;
@end

// 記錄根據(jù)class對(duì)應(yīng)的selector刻诊,并且會(huì)記錄父類關(guān)系防楷,用來(lái)判斷是否在父類或子類中已經(jīng)對(duì)selector進(jìn)行hook。
@interface AspectTracker : NSObject
- (id)initWithTrackedClass:(Class)trackedClass parent:(AspectTracker *)parent;
@property (nonatomic, strong) Class trackedClass;
@property (nonatomic, strong) NSMutableSet *selectorNames;
@property (nonatomic, weak) AspectTracker *parentEntry;
@end

// NSInvotion為消息調(diào)用類坏逢,包含target域帐,selector赘被,參數(shù)和返回值是整。
@interface NSInvocation (Aspects)
- (NSArray *)aspects_arguments;
@end

#define AspectPositionFilter 0x07

#define AspectError(errorCode, errorDescription) do { \
AspectLogError(@"Aspects: %@", errorDescription); \
if (error) { *error = [NSError errorWithDomain:AspectErrorDomain code:errorCode userInfo:@{NSLocalizedDescriptionKey: errorDescription}]; }}while(0)

NSString *const AspectErrorDomain = @"AspectErrorDomain";
static NSString *const AspectsSubclassSuffix = @"_Aspects_";
static NSString *const AspectsMessagePrefix = @"aspects_";

@implementation NSObject (Aspects)

///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Public Aspects API
// 提供的唯二方法肖揣,用來(lái)注冊(cè)方法。  之后的事件根據(jù)事件的調(diào)用來(lái)觸發(fā)浮入。
// block的第一個(gè)參數(shù)為aspectInfo,接下去的參數(shù)為selector的參數(shù).
// 可以對(duì)selector進(jìn)行添加多次龙优,并且一直會(huì)在之前的基礎(chǔ)上新增替換。
+ (id<AspectToken>)aspect_hookSelector:(SEL)selector
                      withOptions:(AspectOptions)options
                       usingBlock:(id)block
                            error:(NSError **)error {
    return aspect_add((id)self, selector, options, block, error);
}

/// @return A token which allows to later deregister the aspect.
- (id<AspectToken>)aspect_hookSelector:(SEL)selector
                      withOptions:(AspectOptions)options
                       usingBlock:(id)block
                            error:(NSError **)error {
    return aspect_add(self, selector, options, block, error);
}

///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Private Helper

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

    __block AspectIdentifier *identifier = nil;
    // 通過(guò)安全鎖來(lái)確保多線程的安全
    aspect_performLocked(^{
        // 判斷是否可以使用黑魔法事秀。 有些方法是不能使用這個(gè)功能的彤断。
        if (aspect_isSelectorAllowedAndTrack(self, selector, options, error)) {
            // 通過(guò)關(guān)聯(lián)對(duì)象添加屬性.一個(gè)selector方法對(duì)應(yīng)一個(gè)容器。
            // 就是說(shuō)易迹,一個(gè)selector可以添加多次宰衙,執(zhí)行前,替換和執(zhí)行后睹欲。
            AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);
            // 創(chuàng)建切面的識(shí)別供炼,這個(gè)包含了注冊(cè)的所有內(nèi)容,包括需要監(jiān)聽(tīng)或替換的selector窘疮,更換為實(shí)現(xiàn)的方法block
            identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error];
            if (identifier) {
                // 在容器中添加需要事件
                [aspectContainer addAspect:identifier withOptions:options];

                // 準(zhǔn)備攔截selector袋哼,為了實(shí)現(xiàn)子類在調(diào)用selector的時(shí)候觸發(fā)消息轉(zhuǎn)發(fā),從而來(lái)調(diào)用別名的實(shí)現(xiàn)闸衫。
                // Modify the class to allow message interception.
                aspect_prepareClassAndHookSelector(self, selector, error);
            }
        }
    });
    return identifier;
}

// 清除注冊(cè)的方法涛贯。通過(guò)token的remove來(lái)執(zhí)行。
static BOOL aspect_remove(AspectIdentifier *aspect, NSError **error) {
    NSCAssert([aspect isKindOfClass:AspectIdentifier.class], @"Must have correct type.");

    __block BOOL success = NO;
    // 線性安全
    aspect_performLocked(^{
        // 局部作用域強(qiáng)引用蔚出,避免在作用域中被提前釋放
        id self = aspect.object; // strongify
        if (self) {
            AspectsContainer *aspectContainer = aspect_getContainerForObject(self, aspect.selector);
            success = [aspectContainer removeAspect:aspect];

            aspect_cleanupHookedClassAndSelector(self, aspect.selector);
            // destroy token
            aspect.object = nil;
            aspect.block = nil;
            aspect.selector = NULL;
        }else {
            NSString *errrorDesc = [NSString stringWithFormat:@"Unable to deregister hook. Object already deallocated: %@", aspect];
            AspectError(AspectErrorRemoveObjectAlreadyDeallocated, errrorDesc);
        }
    });
    return success;
}

// 確保線性安全弟翘,采用了自旋鎖
static void aspect_performLocked(dispatch_block_t block) {
    static OSSpinLock aspect_lock = OS_SPINLOCK_INIT;
    OSSpinLockLock(&aspect_lock);
    block();
    OSSpinLockUnlock(&aspect_lock);
}

// 將selector設(shè)置一個(gè)別名
static SEL aspect_aliasForSelector(SEL selector) {
    NSCParameterAssert(selector);
    return NSSelectorFromString([AspectsMessagePrefix stringByAppendingFormat:@"_%@", NSStringFromSelector(selector)]);
}

// 把block創(chuàng)建一個(gè)方法簽名
static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error) {
    // 通過(guò)使用__bridge將id指針轉(zhuǎn)化為void *,即OC轉(zhuǎn)化為C的用法
    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];
}

// block生成的簽名和selector生成的簽名進(jìn)行判斷骄酗,看是否匹配稀余。
static BOOL aspect_isCompatibleBlockSignature(NSMethodSignature *blockSignature, id object, SEL selector, NSError **error) {
    NSCParameterAssert(blockSignature);
    NSCParameterAssert(object);
    NSCParameterAssert(selector);

    BOOL signaturesMatch = YES;
    NSMethodSignature *methodSignature = [[object class] instanceMethodSignatureForSelector:selector];
    if (blockSignature.numberOfArguments > methodSignature.numberOfArguments) {
        signaturesMatch = NO;
    }else {
        if (blockSignature.numberOfArguments > 1) {
            const char *blockType = [blockSignature getArgumentTypeAtIndex:1];
            if (blockType[0] != '@') {
                signaturesMatch = NO;
            }
        }
        // Argument 0 is self/block, argument 1 is SEL or id<AspectInfo>. We start comparing at argument 2.
        // The block can have less arguments than the method, that's ok.
        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:@"Blog signature %@ doesn't match %@.", blockSignature, methodSignature];
        AspectError(AspectErrorIncompatibleBlockSignature, description);
        return NO;
    }
    return YES;
}

///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Class + Selector Preparation

// 判斷impl是否是消息轉(zhuǎn)發(fā)
static BOOL aspect_isMsgForwardIMP(IMP impl) {
    return impl == _objc_msgForward
#if !defined(__arm64__)
    || impl == (IMP)_objc_msgForward_stret
#endif
    ;
}

// 獲取_objc_msgForward的方法。 針對(duì)32位手機(jī)做了特殊處理酥筝。 32位手機(jī)包括iPhone4滚躯,4s,5嘿歌,5c掸掏。從iPhone5s開(kāi)始都是64位設(shè)備。
static IMP aspect_getMsgForwardIMP(NSObject *self, SEL selector) {
    IMP msgForwardIMP = _objc_msgForward;
#if !defined(__arm64__)
    // As an ugly internal runtime implementation detail in the 32bit runtime, we need to determine of the method we hook returns a struct or anything larger than id.
    // https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/000-Introduction/introduction.html
    // https://github.com/ReactiveCocoa/ReactiveCocoa/issues/783
    // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf (Section 5.4)
    Method method = class_getInstanceMethod(self.class, selector);
    const char *encoding = method_getTypeEncoding(method);
    BOOL methodReturnsStructValue = encoding[0] == _C_STRUCT_B;
    if (methodReturnsStructValue) {
        @try {
            NSUInteger valueSize = 0;
            NSGetSizeAndAlignment(encoding, &valueSize, NULL);

            if (valueSize == 1 || valueSize == 2 || valueSize == 4 || valueSize == 8) {
                methodReturnsStructValue = NO;
            }
        } @catch (NSException *e) {}
    }
    if (methodReturnsStructValue) {
        msgForwardIMP = (IMP)_objc_msgForward_stret;
    }
#endif
    return msgForwardIMP;
}

// 準(zhǔn)備攔截selector:創(chuàng)建一個(gè)子類宙帝,針對(duì)selector創(chuàng)建一個(gè)別名丧凤,然后將selector替換為消息轉(zhuǎn)發(fā)
static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError **error) {
    NSCParameterAssert(selector);
    // 根據(jù)當(dāng)前的類創(chuàng)建一個(gè)類,并且替換子類的class方法步脓,然后返回子類
    Class klass = aspect_hookClass(self, error);
    // klass現(xiàn)在是子類愿待,返回子類的實(shí)例方法浩螺。
    Method targetMethod = class_getInstanceMethod(klass, selector);
    // 根據(jù)實(shí)例方法返回實(shí)例方法實(shí)現(xiàn)
    IMP targetMethodIMP = method_getImplementation(targetMethod);
    // 判斷方法實(shí)現(xiàn)是不是消息轉(zhuǎn)發(fā),如果是消息轉(zhuǎn)發(fā)則不做處理仍侥。
    if (!aspect_isMsgForwardIMP(targetMethodIMP)) {
        // 獲取方法的類型
        // Make a method alias for the existing method implementation, it not already copied.
        const char *typeEncoding = method_getTypeEncoding(targetMethod);
        // 獲取selector的別名
        SEL aliasSelector = aspect_aliasForSelector(selector);
        // 在已開(kāi)始注冊(cè)selector的時(shí)候要出,通過(guò)aspect_getContainerForObject對(duì)selector創(chuàng)建給一個(gè)容器。容器中保存了鉤子的selector农渊,并根據(jù)執(zhí)行前后患蹂,會(huì)依次執(zhí)行。
        // 不類不響應(yīng)這個(gè)selector砸紊,那么就報(bào)錯(cuò)
        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);
        }

        // 使用_objc_msgForward替換了類的selector
        // We use forwardInvocation to hook in.
        class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);
        AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
    }
}

// 清除容器中的切面
// Will undo the runtime changes made.
static void aspect_cleanupHookedClassAndSelector(NSObject *self, SEL selector) {
    NSCParameterAssert(self);
    NSCParameterAssert(selector);

    Class klass = object_getClass(self);
    BOOL isMetaClass = class_isMetaClass(klass);
    if (isMetaClass) {
        klass = (Class)self;
    }

    // Check if the method is marked as forwarded and undo that.
    Method targetMethod = class_getInstanceMethod(klass, selector);
    IMP targetMethodIMP = method_getImplementation(targetMethod);
    // selector方法是_objc_msgForward传于,則將方法再還回來(lái)。
    // 即注冊(cè)的時(shí)候?qū)orwardInvocation替換為_(kāi)_aspects_forwardInvocation:,也就是系統(tǒng)調(diào)用forwardInvocation時(shí)執(zhí)行的是__aspects_forwardInvocation:醉顽,
    // 現(xiàn)在將forwardInvocation和__aspects_forwardInvocation再替換一次沼溜。
    if (aspect_isMsgForwardIMP(targetMethodIMP)) {
        // Restore the original method implementation.
        const char *typeEncoding = method_getTypeEncoding(targetMethod);
        // selector的別名。容器中保存的都是別名的游添。
        SEL aliasSelector = aspect_aliasForSelector(selector);
        Method originalMethod = class_getInstanceMethod(klass, aliasSelector);
        IMP originalIMP = method_getImplementation(originalMethod);
        NSCAssert(originalMethod, @"Original implementation for %@ not found %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass);

        class_replaceMethod(klass, selector, originalIMP, typeEncoding);
        AspectLog(@"Aspects: Removed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
    }

    // 刪除selector在全局中切面痕跡
    // Deregister global tracked selector
    aspect_deregisterTrackedSelector(self, selector);

    // 根據(jù)selector獲取容器
    // Get the aspect container and check if there are any hooks remaining. Clean up if there are not.
    AspectsContainer *container = aspect_getContainerForObject(self, selector);
    if (!container.hasAspects) {
        // 當(dāng)容器已經(jīng)沒(méi)有切面的時(shí)候系草,進(jìn)行銷毀容器
        // Destroy the container
        aspect_destroyContainerForObject(self, selector);

        // Figure out how the class was modified to undo the changes.
        NSString *className = NSStringFromClass(klass);
        if ([className hasSuffix:AspectsSubclassSuffix]) {
            // 在注冊(cè)時(shí)添加的子類,現(xiàn)在恢復(fù)
            Class originalClass = NSClassFromString([className stringByReplacingOccurrencesOfString:AspectsSubclassSuffix withString:@""]);
            NSCAssert(originalClass != nil, @"Original class must exist");
            object_setClass(self, originalClass);
            AspectLog(@"Aspects: %@ has been restored.", NSStringFromClass(originalClass));

            // We can only dispose the class pair if we can ensure that no instances exist using our subclass.
            // Since we don't globally track this, we can't ensure this - but there's also not much overhead in keeping it around.
            //objc_disposeClassPair(object.class);
        }else {
            // Class is most likely swizzled in place. Undo that.
            if (isMetaClass) {
                aspect_undoSwizzleClassInPlace((Class)self);
            }
        }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Hook Class

// 根據(jù)當(dāng)前的類創(chuàng)建一個(gè)類否淤,并且替換子類的class方法悄但,然后返回子類
static Class aspect_hookClass(NSObject *self, NSError **error) {
    NSCParameterAssert(self);
    // 獲取的當(dāng)前的類
    Class statedClass = self.class;
    Class baseClass = object_getClass(self);
    NSString *className = NSStringFromClass(baseClass);

    // 已經(jīng)是aspect的方法了
    // Already subclassed
    if ([className hasSuffix:AspectsSubclassSuffix]) {
        return baseClass;

        // We swizzle a class object, not a single object.
    }else if (class_isMetaClass(baseClass)) {
        // 是類元方法就進(jìn)行方法的替換
        return aspect_swizzleClassInPlace((Class)self);
        // Probably a KVO'ed class. Swizzle in place. Also swizzle meta classes in place.
    }else if (statedClass != baseClass) {
        // 類不同,就進(jìn)行替換石抡。使用baseClass也就是這個(gè)類的實(shí)際類
        return aspect_swizzleClassInPlace(baseClass);
    }

    // Default case. Create dynamic subclass.
    const char *subclassName = [className stringByAppendingString:AspectsSubclassSuffix].UTF8String;
    Class subclass = objc_getClass(subclassName);

    if (subclass == nil) {
        // 創(chuàng)建一個(gè)子類
        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;
        }

        // 替換forwardInvocation方法
        aspect_swizzleForwardInvocation(subclass);
        // 重寫(xiě)subclass的class方法檐嚣,返回statedClass的class方法
        aspect_hookedGetClass(subclass, statedClass);
        // 重寫(xiě)subclass的class方法,返回statedClass的class方法
        // object_getClass 返回的子類的運(yùn)行時(shí)類
        aspect_hookedGetClass(object_getClass(subclass), statedClass);
        // 注冊(cè)動(dòng)態(tài)創(chuàng)建的子類
        objc_registerClassPair(subclass);
    }

    // 修改當(dāng)前的類為動(dòng)態(tài)生成的子類啰扛。
    object_setClass(self, subclass);
    
    return subclass;
}

// 替換forwardInvocation方法
static NSString *const AspectsForwardInvocationSelectorName = @"__aspects_forwardInvocation:";
static void aspect_swizzleForwardInvocation(Class klass) {
    NSCParameterAssert(klass);
    // 把forwardInvocation方法的實(shí)現(xiàn)替換
    // If there is no method, replace will act like class_addMethod.
    IMP originalImplementation = class_replaceMethod(klass, @selector(forwardInvocation:), (IMP)__ASPECTS_ARE_BEING_CALLED__, "v@:@");
    if (originalImplementation) {
        // 給class添加特定的方法嚎京,和已經(jīng)替換forwardInvocation后的實(shí)現(xiàn)
        class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, "v@:@");
    }
    AspectLog(@"Aspects: %@ is now aspect aware.", NSStringFromClass(klass));
}

// 將__aspects_forwardInvocation替換為forwardInvocation
static void aspect_undoSwizzleForwardInvocation(Class klass) {
    NSCParameterAssert(klass);
    Method originalMethod = class_getInstanceMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName));
    Method objectMethod = class_getInstanceMethod(NSObject.class, @selector(forwardInvocation:));
    // There is no class_removeMethod, so the best we can do is to retore the original implementation, or use a dummy.
    IMP originalImplementation = method_getImplementation(originalMethod ?: objectMethod);
    class_replaceMethod(klass, @selector(forwardInvocation:), originalImplementation, "v@:@");

    AspectLog(@"Aspects: %@ has been restored.", NSStringFromClass(klass));
}

// 模仿KVO重寫(xiě)了class實(shí)例方法,將子類的class方法返回原類的class隐解。
// class是新創(chuàng)建的子類鞍帝; statedClass 是原本的類
static void aspect_hookedGetClass(Class class, Class statedClass) {
    NSCParameterAssert(class);
    NSCParameterAssert(statedClass);
    // 獲取創(chuàng)建子類的實(shí)例方法
    Method method = class_getInstanceMethod(class, @selector(class));
    // 方法的實(shí)現(xiàn),實(shí)現(xiàn)的內(nèi)容就是返回原本的類
    IMP newIMP = imp_implementationWithBlock(^(id self) {
        return statedClass;
    });
    // 把class中的@selector(class)用newIMP替換
    class_replaceMethod(class, @selector(class), newIMP, method_getTypeEncoding(method));
}

///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Swizzle Class In Place

// 單例創(chuàng)建一個(gè)swizzledClasses
static void _aspect_modifySwizzledClasses(void (^block)(NSMutableSet *swizzledClasses)) {
    static NSMutableSet *swizzledClasses;
    static dispatch_once_t pred;
    dispatch_once(&pred, ^{
        swizzledClasses = [NSMutableSet new];
    });
    @synchronized(swizzledClasses) {
        block(swizzledClasses);
    }
}

// 把類的forwardInvocation方法替換了
static Class aspect_swizzleClassInPlace(Class klass) {
    NSCParameterAssert(klass);
    NSString *className = NSStringFromClass(klass);

    _aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) {
        // 判斷單例swizzledClasses中是否包含煞茫,如果不包含就添加
        if (![swizzledClasses containsObject:className]) {
            // 給類添加一個(gè)指定的方法和替換forwardInvocation的實(shí)現(xiàn)
            aspect_swizzleForwardInvocation(klass);
            [swizzledClasses addObject:className];
        }
    });
    return klass;
}

// 從全局容器中刪除類對(duì)應(yīng)的容器
static void aspect_undoSwizzleClassInPlace(Class klass) {
    NSCParameterAssert(klass);
    NSString *className = NSStringFromClass(klass);

    _aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) {
        if ([swizzledClasses containsObject:className]) {
            aspect_undoSwizzleForwardInvocation(klass);
            [swizzledClasses removeObject:className];
        }
    });
}

///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Aspect Invoke Point

// invocation開(kāi)始調(diào)用
// This is a macro so we get a cleaner stack trace.
#define aspect_invoke(aspects, info) \
for (AspectIdentifier *aspect in aspects) {\
    [aspect invokeWithInfo:info];\
    if (aspect.options & AspectOptionAutomaticRemoval) { \
        aspectsToRemove = [aspectsToRemove?:@[] arrayByAddingObject:aspect]; \
    } \
}

// 替換forwardInvocation方法帕涌,實(shí)現(xiàn)了自定義的forwardInvocation方法
// This is the swizzled forwardInvocation: method.
static void __ASPECTS_ARE_BEING_CALLED__(__unsafe_unretained NSObject *self, SEL selector, NSInvocation *invocation) {
    NSCParameterAssert(self);
    NSCParameterAssert(invocation);
    SEL originalSelector = invocation.selector;
    SEL aliasSelector = aspect_aliasForSelector(invocation.selector);
    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;

    // Before hooks.
    aspect_invoke(classContainer.beforeAspects, info);
    aspect_invoke(objectContainer.beforeAspects, info);

    // Instead hooks.
    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])) {
                [invocation invoke];
                break;
            }
        }while (!respondsToAlias && (klass = class_getSuperclass(klass)));
    }

    // After hooks.
    aspect_invoke(classContainer.afterAspects, info);
    aspect_invoke(objectContainer.afterAspects, info);

    // If no hooks are installed, call original implementation (usually to throw an exception)
    if (!respondsToAlias) {
        invocation.selector = originalSelector;
        // 調(diào)用__aspects_forwardInvocation:方法,因?yàn)橐呀?jīng)替換了续徽,所以調(diào)用的是之前的舊的方法
        SEL originalForwardInvocationSEL = NSSelectorFromString(AspectsForwardInvocationSelectorName);
        if ([self respondsToSelector:originalForwardInvocationSEL]) {
            ((void( *)(id, SEL, NSInvocation *))objc_msgSend)(self, originalForwardInvocationSEL, invocation);
        }else {
            [self doesNotRecognizeSelector:invocation.selector];
        }
    }

    // Remove any hooks that are queued for deregistration.
    [aspectsToRemove makeObjectsPerformSelector:@selector(remove)];
}
#undef aspect_invoke

///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Aspect Container Management

// 加載或創(chuàng)建容器蚓曼。 通過(guò)關(guān)聯(lián)對(duì)象添加屬性.一個(gè)selector方法對(duì)應(yīng)一個(gè)容器。
// Loads or creates the aspect container.
static AspectsContainer *aspect_getContainerForObject(NSObject *self, SEL selector) {
    NSCParameterAssert(self);
    SEL aliasSelector = aspect_aliasForSelector(selector);
    AspectsContainer *aspectContainer = objc_getAssociatedObject(self, aliasSelector);
    if (!aspectContainer) {
        aspectContainer = [AspectsContainer new];
        objc_setAssociatedObject(self, aliasSelector, aspectContainer, OBJC_ASSOCIATION_RETAIN);
    }
    return aspectContainer;
}

// 根據(jù)selector的別名來(lái)獲取容器钦扭,如果沒(méi)有指定容器則返回nil纫版。
static AspectsContainer *aspect_getContainerForClass(Class klass, SEL selector) {
    NSCParameterAssert(klass);
    AspectsContainer *classContainer = nil;
    do {
        classContainer = objc_getAssociatedObject(klass, selector);
        if (classContainer.hasAspects) break;
    }while ((klass = class_getSuperclass(klass)));

    return classContainer;
}

// 設(shè)置別名selector的容器為nil。
static void aspect_destroyContainerForObject(id<NSObject> self, SEL selector) {
    NSCParameterAssert(self);
    SEL aliasSelector = aspect_aliasForSelector(selector);
    objc_setAssociatedObject(self, aliasSelector, nil, OBJC_ASSOCIATION_RETAIN);
}

///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - Selector Blacklist Checking

// 黑魔法的集合客情,僅僅在進(jìn)程中保持一份其弊。key為class癞己,value為AspectTracker
static NSMutableDictionary *aspect_getSwizzledClassesDict() {
    static NSMutableDictionary *swizzledClassesDict;
    static dispatch_once_t pred;
    dispatch_once(&pred, ^{
        swizzledClassesDict = [NSMutableDictionary new];
    });
    return swizzledClassesDict;
}

// 判斷selector方法是可以被鉤子并添加痕跡
static BOOL aspect_isSelectorAllowedAndTrack(NSObject *self, SEL selector, AspectOptions options, NSError **error) {
    static NSSet *disallowedSelectorList;
    static dispatch_once_t pred;
    dispatch_once(&pred, ^{
        disallowedSelectorList = [NSSet setWithObjects:@"retain", @"release", @"autorelease", @"forwardInvocation:", nil];
    });

    // 如果包含在黑名單中,那么就直接返回
    // Check against the blacklist.
    NSString *selectorName = NSStringFromSelector(selector);
    if ([disallowedSelectorList containsObject:selectorName]) {
        NSString *errorDescription = [NSString stringWithFormat:@"Selector %@ is blacklisted.", selectorName];
        AspectError(AspectErrorSelectorBlacklisted, errorDescription);
        return NO;
    }

    // 如果selector是dealloc并且position不是before梭伐,那么就報(bào)錯(cuò)痹雅。
    // Additional checks.
    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;
    }

    // 如果selector不響應(yīng)也報(bào)錯(cuò)
    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;
    }

    // 查找當(dāng)前類和類的層次,是否已經(jīng)修改了籽御。如果已經(jīng)修改并沒(méi)有parent练慕,則返回YES惰匙,不然返回NO技掏。也就是說(shuō)在類的層級(jí)中,只要一個(gè)class添加了hook项鬼,那么在子類或父類中就不能再添加方法的hook哑梳。
    // Search for the current class and the class hierarchy IF we are modifying a class object
    if (class_isMetaClass(object_getClass(self))) {
        Class klass = [self class];
        NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict();
        Class currentClass = [self class];
        do {
            AspectTracker *tracker = swizzledClassesDict[currentClass];
            if ([tracker.selectorNames containsObject:selectorName]) {

                // Find the topmost class for the log.
                if (tracker.parentEntry) {
                    AspectTracker *topmostEntry = tracker.parentEntry;
                    while (topmostEntry.parentEntry) {
                        topmostEntry = topmostEntry.parentEntry;
                    }
                    NSString *errorDescription = [NSString stringWithFormat:@"Error: %@ already hooked in %@. A method can only be hooked once per class hierarchy.", selectorName, NSStringFromClass(topmostEntry.trackedClass)];
                    AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription);
                    return NO;
                }else if (klass == currentClass) {
                    // Already modified and topmost!
                    return YES;
                }
            }
        }while ((currentClass = class_getSuperclass(currentClass)));

        // 記錄需要鉤子的類,創(chuàng)建tracker對(duì)象保存到黑魔法的集合中绘盟。 并會(huì)遍歷整個(gè)類層級(jí)鸠真,在每一個(gè)類中添加tracker。
        // Add the selector as being modified.
        currentClass = klass;
        AspectTracker *parentTracker = nil;
        do {
            AspectTracker *tracker = swizzledClassesDict[currentClass];
            if (!tracker) {
                tracker = [[AspectTracker alloc] initWithTrackedClass:currentClass parent:parentTracker];
                swizzledClassesDict[(id<NSCopying>)currentClass] = tracker;
            }
            [tracker.selectorNames addObject:selectorName];
            // 如果一個(gè)類中的方法進(jìn)行了鉤子龄毡,那么在所有的父類中進(jìn)行記錄其子類中已經(jīng)有方法進(jìn)行修改了吠卷。
            // All superclasses get marked as having a subclass that is modified.
            parentTracker = tracker;
        }while ((currentClass = class_getSuperclass(currentClass)));
    }

    return YES;
}

// 從容器中刪除切面痕跡類,一直遍歷所有父類并逐一刪除沦零。
static void aspect_deregisterTrackedSelector(id self, SEL selector) {
    if (!class_isMetaClass(object_getClass(self))) return;

    NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict();
    NSString *selectorName = NSStringFromSelector(selector);
    Class currentClass = [self class];
    do {
        AspectTracker *tracker = swizzledClassesDict[currentClass];
        if (tracker) {
            [tracker.selectorNames removeObject:selectorName];
            if (tracker.selectorNames.count == 0) {
                [swizzledClassesDict removeObjectForKey:tracker];
            }
        }
    }while ((currentClass = class_getSuperclass(currentClass)));
}

@end

@implementation AspectTracker

// 初始化跟蹤
- (id)initWithTrackedClass:(Class)trackedClass parent:(AspectTracker *)parent {
    if (self = [super init]) {
        _trackedClass = trackedClass;
        _parentEntry = parent;
        _selectorNames = [NSMutableSet new];
    }
    return self;
}
- (NSString *)description {
    return [NSString stringWithFormat:@"<%@: %@, trackedClass: %@, selectorNames:%@, parent:%p>", self.class, self, NSStringFromClass(self.trackedClass), self.selectorNames, self.parentEntry];
}

@end

///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - NSInvocation (Aspects)

@implementation NSInvocation (Aspects)

// NSInvocation的分類獲取指定索引的參數(shù)
// Thanks to the ReactiveCocoa team for providing a generic solution for this.
- (id)aspect_argumentAtIndex:(NSUInteger)index {
    const char *argType = [self.methodSignature getArgumentTypeAtIndex:index];
    // Skip const type qualifier.
    if (argType[0] == _C_CONST) argType++;

#define WRAP_AND_RETURN(type) do { type val = 0; [self getArgument:&val atIndex:(NSInteger)index]; return @(val); } while (0)
    if (strcmp(argType, @encode(id)) == 0 || strcmp(argType, @encode(Class)) == 0) {
        __autoreleasing id returnObj;
        [self getArgument:&returnObj atIndex:(NSInteger)index];
        return returnObj;
    } else if (strcmp(argType, @encode(SEL)) == 0) {
        SEL selector = 0;
        [self getArgument:&selector atIndex:(NSInteger)index];
        return NSStringFromSelector(selector);
    } else if (strcmp(argType, @encode(Class)) == 0) {
        __autoreleasing Class theClass = Nil;
        [self getArgument:&theClass atIndex:(NSInteger)index];
        return theClass;
        // Using this list will box the number with the appropriate constructor, instead of the generic NSValue.
    } else if (strcmp(argType, @encode(char)) == 0) {
        WRAP_AND_RETURN(char);
    } else if (strcmp(argType, @encode(int)) == 0) {
        WRAP_AND_RETURN(int);
    } else if (strcmp(argType, @encode(short)) == 0) {
        WRAP_AND_RETURN(short);
    } else if (strcmp(argType, @encode(long)) == 0) {
        WRAP_AND_RETURN(long);
    } else if (strcmp(argType, @encode(long long)) == 0) {
        WRAP_AND_RETURN(long long);
    } else if (strcmp(argType, @encode(unsigned char)) == 0) {
        WRAP_AND_RETURN(unsigned char);
    } else if (strcmp(argType, @encode(unsigned int)) == 0) {
        WRAP_AND_RETURN(unsigned int);
    } else if (strcmp(argType, @encode(unsigned short)) == 0) {
        WRAP_AND_RETURN(unsigned short);
    } else if (strcmp(argType, @encode(unsigned long)) == 0) {
        WRAP_AND_RETURN(unsigned long);
    } else if (strcmp(argType, @encode(unsigned long long)) == 0) {
        WRAP_AND_RETURN(unsigned long long);
    } else if (strcmp(argType, @encode(float)) == 0) {
        WRAP_AND_RETURN(float);
    } else if (strcmp(argType, @encode(double)) == 0) {
        WRAP_AND_RETURN(double);
    } else if (strcmp(argType, @encode(BOOL)) == 0) {
        WRAP_AND_RETURN(BOOL);
    } else if (strcmp(argType, @encode(bool)) == 0) {
        WRAP_AND_RETURN(BOOL);
    } else if (strcmp(argType, @encode(char *)) == 0) {
        WRAP_AND_RETURN(const char *);
    } else if (strcmp(argType, @encode(void (^)(void))) == 0) {
        __unsafe_unretained id block = nil;
        [self getArgument:&block atIndex:(NSInteger)index];
        return [block copy];
    } else {
        NSUInteger valueSize = 0;
        NSGetSizeAndAlignment(argType, &valueSize, NULL);

        unsigned char valueBytes[valueSize];
        [self getArgument:valueBytes atIndex:(NSInteger)index];

        return [NSValue valueWithBytes:valueBytes objCType:argType];
    }
    return nil;
#undef WRAP_AND_RETURN
}

- (NSArray *)aspects_arguments {
    NSMutableArray *argumentsArray = [NSMutableArray array];
    for (NSUInteger idx = 2; idx < self.methodSignature.numberOfArguments; idx++) {
        [argumentsArray addObject:[self aspect_argumentAtIndex:idx] ?: NSNull.null];
    }
    return [argumentsArray copy];
}

@end

///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - AspectIdentifier

@implementation AspectIdentifier

// 創(chuàng)建切面的識(shí)別
+ (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError **)error {
    NSCParameterAssert(block);
    NSCParameterAssert(selector);
    // block生成一個(gè)簽名
    NSMethodSignature *blockSignature = aspect_blockMethodSignature(block, error);
    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;
}

// 將原來(lái)想替換的block中參數(shù)設(shè)置到根據(jù)aspect_add傳入block生成的block簽名中祭隔,然后block簽名執(zhí)行aspect_add傳入的block。
- (BOOL)invokeWithInfo:(id<AspectInfo>)info {
    NSInvocation *blockInvocation = [NSInvocation invocationWithMethodSignature:self.blockSignature];
    NSInvocation *originalInvocation = info.originalInvocation;
    NSUInteger numberOfArguments = self.blockSignature.numberOfArguments;

    // Be extra paranoid. We already check that on hook registration.
    if (numberOfArguments > originalInvocation.methodSignature.numberOfArguments) {
        AspectLogError(@"Block has too many arguments. Not calling %@", info);
        return NO;
    }

    // The `self` of the block will be the AspectInfo. Optional.
    if (numberOfArguments > 1) {
        [blockInvocation setArgument:&info atIndex:1];
    }
    
    void *argBuf = NULL;
    for (NSUInteger idx = 2; idx < numberOfArguments; idx++) {
        const char *type = [originalInvocation.methodSignature getArgumentTypeAtIndex:idx];
        NSUInteger argSize;
        NSGetSizeAndAlignment(type, &argSize, NULL);
        
        if (!(argBuf = reallocf(argBuf, argSize))) {
            AspectLogError(@"Failed to allocate memory for block invocation.");
            return NO;
        }
        
        [originalInvocation getArgument:argBuf atIndex:idx];
        [blockInvocation setArgument:argBuf atIndex:idx];
    }
    
    [blockInvocation invokeWithTarget:self.block];
    
    if (argBuf != NULL) {
        free(argBuf);
    }
    return YES;
}

- (NSString *)description {
    return [NSString stringWithFormat:@"<%@: %p, SEL:%@ object:%@ options:%tu block:%@ (#%tu args)>", self.class, self, NSStringFromSelector(self.selector), self.object, self.options, self.block, self.blockSignature.numberOfArguments];
}

// 刪除鉤子的方法
- (BOOL)remove {
    return aspect_remove(self, NULL);
}

@end

///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - AspectsContainer

@implementation AspectsContainer

// 是否有切面識(shí)別
- (BOOL)hasAspects {
    return self.beforeAspects.count > 0 || self.insteadAspects.count > 0 || self.afterAspects.count > 0;
}

// 根據(jù)options將切面識(shí)別 添加到對(duì)應(yīng)的數(shù)組中
- (void)addAspect:(AspectIdentifier *)aspect withOptions:(AspectOptions)options {
    NSParameterAssert(aspect);
    NSUInteger position = options&AspectPositionFilter;
    switch (position) {
        case AspectPositionBefore:  self.beforeAspects  = [(self.beforeAspects ?:@[]) arrayByAddingObject:aspect]; break;
        case AspectPositionInstead: self.insteadAspects = [(self.insteadAspects?:@[]) arrayByAddingObject:aspect]; break;
        case AspectPositionAfter:   self.afterAspects   = [(self.afterAspects  ?:@[]) arrayByAddingObject:aspect]; break;
    }
}

// 從容器中移除切面識(shí)別
- (BOOL)removeAspect:(id)aspect {
    for (NSString *aspectArrayName in @[NSStringFromSelector(@selector(beforeAspects)),
                                        NSStringFromSelector(@selector(insteadAspects)),
                                        NSStringFromSelector(@selector(afterAspects))]) {
        NSArray *array = [self valueForKey:aspectArrayName];
        NSUInteger index = [array indexOfObjectIdenticalTo:aspect];
        if (array && index != NSNotFound) {
            NSMutableArray *newArray = [NSMutableArray arrayWithArray:array];
            [newArray removeObjectAtIndex:index];
            [self setValue:newArray forKey:aspectArrayName];
            return YES;
        }
    }
    return NO;
}

- (NSString *)description {
    return [NSString stringWithFormat:@"<%@: %p, before:%@, instead:%@, after:%@>", self.class, self, self.beforeAspects, self.insteadAspects, self.afterAspects];
}

@end

///////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - AspectInfo

@implementation AspectInfo

@synthesize arguments = _arguments;
// 初始化切面信息
- (id)initWithInstance:(__unsafe_unretained id)instance invocation:(NSInvocation *)invocation {
    NSCParameterAssert(instance);
    NSCParameterAssert(invocation);
    if (self = [super init]) {
        _instance = instance;
        _originalInvocation = invocation;
    }
    return self;
}

- (NSArray *)arguments {
    // Lazily evaluate arguments, boxing is expensive.
    if (!_arguments) {
        _arguments = self.originalInvocation.aspects_arguments;
    }
    return _arguments;
}

@end

參考

  1. http://www.reibang.com/p/1bc8205c0904
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末路操,一起剝皮案震驚了整個(gè)濱河市疾渴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌屯仗,老刑警劉巖搞坝,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異魁袜,居然都是意外死亡桩撮,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)峰弹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)店量,“玉大人,你說(shuō)我怎么就攤上這事垮卓〉婀穑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵粟按,是天一觀的道長(zhǎng)诬滩。 經(jīng)常有香客問(wèn)我霹粥,道長(zhǎng)蓬蝶,這世上最難降的妖魔是什么唯沮? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任响巢,我火速辦了婚禮蒸苇,結(jié)果婚禮上蓬抄,老公的妹妹穿的比我還像新娘杏糙。我一直安慰自己痘绎,他們只是感情好洽损,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布吴攒。 她就那樣靜靜地躺著张抄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪洼怔。 梳的紋絲不亂的頭發(fā)上署惯,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音镣隶,去河邊找鬼极谊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛安岂,可吹牛的內(nèi)容都是我干的轻猖。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼域那,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼咙边!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起琉雳,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤样眠,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后翠肘,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體檐束,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年束倍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了被丧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绪妹,死狀恐怖甥桂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情邮旷,我是刑警寧澤黄选,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響办陷,放射性物質(zhì)發(fā)生泄漏貌夕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一民镜、第九天 我趴在偏房一處隱蔽的房頂上張望啡专。 院中可真熱鬧,春花似錦制圈、人聲如沸们童。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)慧库。三九已至,卻和暖如春亥鬓,著一層夾襖步出監(jiān)牢的瞬間完沪,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工嵌戈, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人听皿。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓熟呛,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親尉姨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子庵朝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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