Aspects源码分析

栏目: IOS · 发布时间: 5年前

内容简介:大家可以试试以下代码结果是,通过

Aspects 一直在项目中使用,主要使用AOP方式进行tracking,但还没好好看一看,最近研究了一下源码,十分推荐大家阅读一下,如果只是一味的看 Runtime 源码,很难真正的掌握还容易忘,配合着看像 Aspects 这样优秀的框架有助于形成知识体系,而且代码量也不大,本文只是分析主要的一些源码,可以到我的github上看更多源码注释 AspectsAnalysis

NSMethodSignature和NSInvocation

大家可以试试以下代码

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    // 获取某个类的实例方法签名有两种
    NSMethodSignature *signature = [self methodSignatureForSelector:@selector(test:)];
//    NSMethodSignature *signature = [ViewController instanceMethodSignatureForSelector:@selector(test:)];

    // 获取某个类的类方法只有一种
//    NSMethodSignature *signature = [ViewController methodSignatureForSelector:@selector(test:)];

    // 获取方法签名对应的invocation
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];

    // 设置消息接受者,与[invocation setArgument:(__bridge void * _Nonnull)(self) atIndex:0]等价
    [invocation setTarget:self];

    // 设置要执行的selector, 与[invocation setArgument:@selector(test:) atIndex:1]
    [invocation setSelector:@selector(test:)];

    //设置参数
    NSString *str = @"hello world";
    [invocation setArgument:&str atIndex:2];

    //开始执行
    [invocation invoke];
}

- (void)test:(NSString*)string{
    NSLog(@"test  %@",string);
}


复制代码

结果是,通过 NSMethodSignatureNSInvocation 也能完成实例对象的方法调用

2019-04-20 16:00:57.027828+0800 Test[7439:627386] test  hello world


复制代码

NSMethodSignature

一个 NSMethodSignature 对象记录着某个方法的返回值类型信息以及参数类型信息。它用于转发消息接收者无法响应的消息。

上面代码中提供了获得类方法和实例方法签名的方式,也可以使用 signatureWithObjCTypes 创建方法签名。

@interface NSMethodSignature
+ (nullable NSMethodSignature *)signatureWithObjCTypes:(const char *)types;
@end
复制代码

NSMethodSignature对象是根据字符串创建的,这里的字符串代表了某个方法的返回值类型以及参数类型的字符串编码( Objective-C type encodings )。

个方法签名包含代表方法返回值的一个或多个字符,后面跟上隐式参数self以及_cmd的字符串编码,然后后面再跟上零个或多个明确的参数的字符编码。可以通过methodReturnType属性获取返回值类型的字符编码,可以通过methodReturnLength属性获取返回值类型的长度。

例如:NSString的实例方法containsString:的方法签名包含以下参数:

BOOL

NSInvocation

NSInvocation 封装了方法调用对象、方法选择器、参数、返回值等,可以给对象发送一个参数大于两个的消息,可以直接设置这些元素中的每一个,并在 NSInvocation 调度对象时自动设置返回值。

Objective-C方法调用过程

Objective-C 的方法调用过程中,如果 selector 有对应的 IMP ,则直接执行。如果没有,在抛出异常之前还有一些弥补机会,依次有 resolveInstanceMethodforwardingTargetForSelectorforwardInvocatio

  • resolveInstanceMethod (或resolveClassMethod) :实现该方法,可以通过 class_addMethod 添加方法,返回YES的话系统在运行时就会重新启动一次消息发送的过程,NO的话会继续执行下一个方法。
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(runTo:)) {
        class_addMethod(self, sel, (IMP)dynamicMethodIMPRunTo, "v@:@");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
//动态添加的@selector(runTo:) 对应的实现
static void dynamicMethodIMPRunTo(id self, SEL _cmd,id place){
    NSLog(@"dynamicMethodIMPRunTo %@",place);
}


复制代码
  • forwardingTargetForSelector :实现该方法可以将消息转发给其他对象,只要这个方法返回的不是 nilself ,也会重启消息发送的过程,把这消息转发给其他对象来处理。
-(id)forwardingTargetForSelector:(SEL)aSelector{
    if (aSelector == @selector(dynamicSelector) && [self.myObj respondsToSelector:@selector(dynamicSelector)]) {
        return self.myObj;
    }else{
        return [super forwardingTargetForSelector:aSelector];
    }
}

复制代码

如果上面两步都无法完成这个 SEL 的处理,就会通过 forwardInvocation 进行消息转发

  • methodSignatureForSelector :会去获取一个方法签名,如果没有获取到的话就回直接挑用 doesNotRecognizeSelector ,如果能获取的话系统就会创建一个 NSlnvocation 传给 forwardInvocation 方法。
- (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector{
    //判断selector是否为需要转发的,如果是则手动生成方法签名并返回。
    if (aSelector == @selector(dynamicSelector)){
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    }
    return [super forwardingTargetForSelector:aSelector];
}

复制代码
  • forwardInvocation :该方法是上一个步传进来的 NSlnvocation ,然后调用 NSlnvocationinvokeWithTarget 方法,转发到对应的 Target
- (void)forwardInvocation:(NSInvocation *)anInvocation{
    //判断待处理的anInvocation是否为我们要处理的
    if (anInvocation.selector == @selector(dynamicSelector)){
    		
    }else{
    }
}


复制代码
  • doesNotRecognizeSelector :抛出 unrecognized selector sent to … 异常

上面描述的是正常的方法调用过程,如果想手动出发消息转发怎么办呢? _objc_msgForward 或者 _objc_msgForward_stret ,那他们区别是什么呢?

IMP msgForwardIMP = _objc_msgForward;
#if !defined(__arm64__)
    if (typeDescription[0] == '{') {
        //In some cases that returns struct, we should use the '_stret' API:
        //http://sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html
        //NSMethodSignature knows the detail but has no API to return, we can only get the info from debugDescription.
        NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:typeDescription];
        if ([methodSignature.debugDescription rangeOfString:@"is special struct return? YES"].location != NSNotFound) {
            msgForwardIMP = (IMP)_objc_msgForward_stret;
        }
    }

复制代码

JSPatch 通过判断方法签名的 debugDescription 是不是包含特定字符串 is special struct return? YES ,进而决定是否使用 _objc_msgForward_stret

JSPatch 作者解释

大多数CPU在执行C函数时会把前几个参数放进寄存器里,对 obj_msgSend 来说前两个参数固定是 self / _cmd,它们会放在寄存器上,在最后执行完后返回值也会保存在寄存器上,取这个寄存器的值就是返回值。普通的返回值(int/pointer)很小,放在寄存器上没问题,但有些 struct 是很大的,寄存器放不下,所以要用另一种方式,在一开始申请一段内存,把指针保存在寄存器上,返回值往这个指针指向的内存写数据,所以寄存器要腾出一个位置放这个指针,self / _cmd 在寄存器的位置就变了。objc_msgSend 不知道 self / _cmd 的位置变了,所以要用另一个方法 objc_msgSend_stret 代替。原理大概就是这样。在 NSMethodSignature 的 debugDescription 上打出了是否 special struct,只能通过这字符串判断。所以最终的处理是,在非 arm64 下,是 special struct 就走 _objc_msgForward_stret,否则走 _objc_msgForward。

static IMP aspect_getMsgForwardIMP(NSObject *self, SEL selector) {
    IMP msgForwardIMP = _objc_msgForward;
#if !defined(__arm64__)
    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 (__unused NSException *e) {}
    }
    if (methodReturnsStructValue) {
        msgForwardIMP = (IMP)_objc_msgForward_stret;
    }
#endif
    return msgForwardIMP;
}

复制代码

Aspects 是判断方法返回值的内存大小,来决定是否使用 _objc_msgForward_stret

Aspects基本原理

  1. 将hook的 selector 指向 objc_msgForward / _objc_msgForward_stret
  2. 生成 aliasSelector 指向原来的 selector 的IMP。
  3. forwardInvocation 指向自定义的 __ASPECTS_ARE_BEING_CALLED__
  4. 生成 __aspects_forwardInvocation 指向原来 forwardInvocation 的IMP
Aspects源码分析

Aspects源码分析

有前面的铺垫,再看源码就会轻松很多,源码只会分析一些比较重要的部分。

Aspects一些内部结构和协议

这里只简单介绍一下,详细结构可以查看源码。

  • AspectToken:用于注销 Hook。
  • AspectInfo:block的第一个参数。
  • AspectIdentifier:每进行一个hook,都会生成一个AspectIdentifier对象,包含:方法,block,签名信息等。
  • AspectsContainer:用于盛放AspectIdentifier对象。一个对象或者类对应一个AspectsContainer对象,有三个数组,beforeAspects,insteadAspects,afterAspects。
  • AspectTracker:每一个class对应一个AspectTracker。 在一个继承链上一个selector只能被hook一次。

_AspectBlock

因为没法直接拿到 block 的签名信息,所以创建 _AspectBlock 目的是拿到block的签名信息,然后就可以使用NSInvocation调用这个block。

我们先来看看苹果源码Block_private.h的 Block 内存结构,是一个结构体。

struct Block_layout {
    void *isa;
    volatile int32_t flags; // contains ref count
    int32_t reserved; 
    void (*invoke)(void *, ...);
    struct Block_descriptor_1 *descriptor;
    // imported variables
};

复制代码
// Values for Block_layout->flags to describe block objects
enum {
    BLOCK_DEALLOCATING =      (0x0001),  // runtime
    BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime
    BLOCK_NEEDS_FREE =        (1 << 24), // runtime
    BLOCK_HAS_COPY_DISPOSE =  (1 << 25), // compiler
    BLOCK_HAS_CTOR =          (1 << 26), // compiler: helpers have C++ code
    BLOCK_IS_GC =             (1 << 27), // runtime
    BLOCK_IS_GLOBAL =         (1 << 28), // compiler
    BLOCK_USE_STRET =         (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE
    BLOCK_HAS_SIGNATURE  =    (1 << 30), // compiler
    BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31)  // compiler
};

复制代码

再来看看 _AspectBlock ,可以很清晰的看到 Aspects 仿照系统定义的。

typedef NS_OPTIONS(int, AspectBlockFlags) {
    // 捕获外界变量
    AspectBlockFlagsHasCopyDisposeHelpers = (1 << 25),
    // 方法有签名信息,Block也有签名信息
    AspectBlockFlagsHasSignature          = (1 << 30)
};
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;

复制代码

aspect_blockMethodSignature 方法是用来获得 blocK 的签名。原理是因为没法直接拿到 block 的签名信息,所以将 block 强制类型转换为 AspectBlockRef ,根据标志位和结构体的结构,获取 signature

static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error) {
    // 将block强制转为AspectBlockRef
    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;
    }
    // descriptor,指针移动
	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];
}
复制代码

这里稍微解释指针移动,我们知道指针是指向一块内存的首地址, desc += 2 * sizeof(unsigned long int); 是因为需要偏移下面两个 unsigned long int) 内存大小。

unsigned long int reserved;     
unsigned long int size;
复制代码

如果捕获外界变量,这两个 Void 指针是有值得,所以需要偏移两个 Void 指针的内存大小 desc += 2 * sizeof(void *);

void (*copy)(void *dst, const void *src);
void (*dispose)(const void *);

复制代码

我们看一看 block 签名信息是什么样

[UIViewController aspect_hookSelector:@selector(viewDidLoad) withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> info){
        NSLog(@"viewDidLoad");
} error:nil];

复制代码
Aspects源码分析

返回值是 Void ,第一个参数是 @? ,表示是 Block ,第二个参数 @"<AspectInfo>" ,表示遵循了 AspectInfo 协议,我们看到 block 签名和方法签名是不同的,所以需要比较签名信息。

aspect_isCompatibleBlockSignature

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

    BOOL signaturesMatch = YES;
    // viewWillAppear: (v @ : c)
    NSMethodSignature *methodSignature = [[object class] instanceMethodSignatureForSelector:selector];
    // block签名参数一定是小于方法签名参数
    if (blockSignature.numberOfArguments > methodSignature.numberOfArguments) {
        signaturesMatch = NO;
    }else {
        if (blockSignature.numberOfArguments > 1) {
            const char *blockType = [blockSignature getArgumentTypeAtIndex:1];
            // 遵循AspectInfo协议对象
            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:@"Block signature %@ doesn't match %@.", blockSignature, methodSignature];
        AspectError(AspectErrorIncompatibleBlockSignature, description);
        return NO;
    }
    return YES;
}

复制代码

从注释可以看到签名信息参数前两位是默认的, Argument 0self/blockargument 1SEL or id<AspectInfo> ,所以从index = 2开始校验。

aspect_add

// 这里的self可以是实例对象 也可以是类对象
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(^{
        // 判断selector是否允许进行hook操作
        // 1."retain"、"release"、"autorelease"、"forwardInvocation"这几个方法是不被允许的。
        // 2.如果方法是dealloc,则他的切入点必须是Before。
        // 3.判断当前实例对象和类对象是否能响应方法。
        // 4.是否是类对象,如果是则判断继承体系中方法是否已经被Hook,而实例则不用。
        if (aspect_isSelectorAllowedAndTrack(self, selector, options, error)) {
            
            // 通过objc_getAssociatedObject 获取selector方法的容器
            AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);
            
            // 将block封装到AspectIdentifier对象中
            identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error];
            if (identifier) {
                // 通过options将identifier添加到容器对应的beforeAspects,insteadAspects,afterAspects数组中
                [aspectContainer addAspect:identifier withOptions:options];
                // HookSelector和HookClass
                aspect_prepareClassAndHookSelector(self, selector, error);
            }
        }
    });
    return identifier;
}
复制代码

aspect_prepareClassAndHookSelector

static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError **error) {
    NSCParameterAssert(selector);
    
    // Hook Class,进行swizzleForwardInvocation
    // klass类为刚创建的具有_Aspects_后缀的子类
    Class klass = aspect_hookClass(self, error);
    // 在创建的时候指定类他的父类,所以我们可以获取到selector这个方法
    Method targetMethod = class_getInstanceMethod(klass, selector);
    IMP targetMethodIMP = method_getImplementation(targetMethod);
    // 如果selector的实现是_objc_msgForward或者_objc_msgForward_stret,就不进行method swizzle 了。
    if (!aspect_isMsgForwardIMP(targetMethodIMP)) {
        // 获得原生方法的类型编码
        const char *typeEncoding = method_getTypeEncoding(targetMethod);
        // 给原selector方法名添加前缀,并返回。 这个添加前缀的selector的实现就是原selector的实现
        SEL aliasSelector = aspect_aliasForSelector(selector);
        if (![klass instancesRespondToSelector:aliasSelector]) {
            // 没有使用aliasSelector 保存selector原来的实现
            __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);
        }

        // 将被hook方法的实现改为forwardInvocation(消息转发)
        class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);
        AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
    }
}

复制代码

总结一下:

  1. forwardInvocation 的实现替换为 自定义方法 __ASPECTS_ARE_BEING_CALLED__ ,并添加 __aspects_forwardInvocation 的实现为 forwardInvocation 原来的实现。 需要进行消息转发的 selector 都会执行 __ASPECTS_ARE_BEING_CALLED__
  2. hookselector 的实现替换为 _objc_msgForward 或者 _objc_msgForward_stret ,同时添加 aspect_aliasForSelector 的实现为 selector 原来的实现。 此时调用 selector 时就会进行消息转发。

aspect_hookClass

static Class aspect_hookClass(NSObject *self, NSError **error) {
    NSCParameterAssert(self);
    // 当self是instance(对象)获取当前Class(类对象),当self是Class(类对象)返回自身
	Class statedClass = self.class;
    // 获取isa指针
	Class baseClass = object_getClass(self);
	NSString *className = NSStringFromClass(baseClass);

    // 当hook一个对象的selector时会生成一个子类,子类前缀就是AspectsSubclassSuffix。当self对应的类就是生成的子类,直接返回
	if ([className hasSuffix:AspectsSubclassSuffix]) {
		return baseClass;
    // 判断是否为类对象,如果是,则直接在当前类中进行swizzle
	}else if (class_isMetaClass(baseClass)) {
        return aspect_swizzleClassInPlace((Class)self);
    // 判断是否为KVO过的对象,因为KVO的对象ISA指针指向一个中间类,则直接在这个间接勒种进行swizzle
    }else if (statedClass != baseClass) {
        return aspect_swizzleClassInPlace(baseClass);
    }

    /** 当hook一个对象的selector时,实现原理与KVO相似。 1,生成一个子类;2,aspect_swizzleForwardInvocation*/
    // 默认情况下,动态创建子类,拼接子类后缀为AspectsSubclassSuffix
	const char *subclassName = [className stringByAppendingString:AspectsSubclassSuffix].UTF8String;
    // 获取子类isa
	Class subclass = objc_getClass(subclassName);

	if (subclass == nil) {
		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的方法实现为__ASPECTS_ARE_BEING_CALLED__
		aspect_swizzleForwardInvocation(subclass);
        // 把生成子类的isa指针指向原生的类
		aspect_hookedGetClass(subclass, statedClass);
        // 把生成子类的元类的isa指向原生的类
		aspect_hookedGetClass(object_getClass(subclass), statedClass);
        // 注册当前生成的子类
		objc_registerClassPair(subclass);
	}

    // 将当前对象的isa指针指向刚生成的类
	object_setClass(self, subclass);
	return subclass;
}
复制代码

总结一下:

  1. 动态创建子类
  2. 将子类的 forwardInvocation 的实现替换成 __ASPECTS_ARE_BEING_CALLED__
  3. 把子类的元类的 isa 和子类的元类的 isa 指向原生的类
  4. 注册子类
  5. self 对象 isa 指针指向子类

图片来自 Aspects关联&调用流程浅析

对某个类的所有实例进行hook

Aspects源码分析

某个类实例进行hook

Aspects源码分析

ASPECTS_ARE_BEING_CALLED

// 消息经过转发后都会来到这里(这里包括手动消息转发和自动消息转发)在这里进行统一的处理: 调用block,执行原方法实现
// This is the swizzled forwardInvocation: method.
static void __ASPECTS_ARE_BEING_CALLED__(__unsafe_unretained NSObject *self, SEL selector, NSInvocation *invocation) {
    NSCParameterAssert(self);
    NSCParameterAssert(invocation);
    // 拿到originalSelector
    SEL originalSelector = invocation.selector;
    // originalSelector 加前缀得到 aliasSelector,含有前缀的方法aspects_
	SEL aliasSelector = aspect_aliasForSelector(invocation.selector);
    // 用 aliasSelector 替换 invocation.selector
    invocation.selector = aliasSelector;
    // Instance 的容器
    AspectsContainer *objectContainer = objc_getAssociatedObject(self, aliasSelector);
    // Class 的容器
    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 {
        // 没有Instead hooks时就执行selector 被hook之前的实现。
        Class klass = object_getClass(invocation.target);
        // 遍历 invocation.target及其superClass找到实例可以响应 aliasSelector的invocation invoke
        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);

    // 调用一个没有实现的selector会触发 自动消息转发,在这种情况下整个继承链中都不会响应aliasSelector也就导致respondsToAlias=false, 开始执行下面的方法
    // If no hooks are installed, call original implementation (usually to throw an exception)
    if (!respondsToAlias) {
        invocation.selector = originalSelector;
        SEL originalForwardInvocationSEL = NSSelectorFromString(AspectsForwardInvocationSelectorName);
        // 如果实现了forwardInvocation,执行原来的消息转发,否则调用doesNotRecognizeSelector,抛出异常。
        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 队列中的 AspectIdentifier,执行 remove
    [aspectsToRemove makeObjectsPerformSelector:@selector(remove)];
}

复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Programming Rust

Programming Rust

Jim Blandy / O'Reilly Media / 2016-8-25 / GBP 47.99

This practical book introduces systems programmers to Rust, the new and cutting-edge language that’s still in the experimental/lab stage. You’ll learn how Rust offers the rare and valuable combination......一起来看看 《Programming Rust》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具