AOP三方框架Aspects

2019-05-15  本文已影响0人  大王叫我来巡山丨

1. 什么是AOP

AOP: Aspect Oriented Programming 面向切面编程。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果(在不修改源代码的情况下,通过运行时给程序添加统一功能!)

2. Aspects实现原理

Aspects是利用了OC的消息转发机制!如果是实例对象,则使用runtime动态创建对象的子类,在子类中使用swizzling method的方式,添加一个别名SEL来记录被hook的SEL,然后把子类或类对象中的forwardInvocation的IMP替成__ASPECTS_ARE_BEING_CALLED__,如果是类对象则只是方法替换,在__ASPECTS_ARE_BEING_CALLED__中进行拦截处理!

消息转发流程

2.1resolveInstanceMethod(或resolveClassMethod):实现该方法,可以通过class_addMethod添加方法,返回YES的话系统在运行时就会重新启动一次消息发送的过程,NO的话会继续执行下一个方法。
2.2 forwardingTargetForSelector:实现该方法可以将消息转发给其他对象,只要这个方法返回的不是nil或self,也会重启消息发送的过程,把这消息转发给其他对象来处理。
2.3 methodSignatureForSelector:会去获取一个方法签名,如果没有获取到的话就直接调用doesNotRecognizeSelector,如果能获取的话系统就会创建一个NSlnvocation传给forwardInvocation方法。
2.4 forwardInvocation:该方法是上一个步传进来的NSlnvocation,然后调用NSlnvocationinvokeWithTarget方法,转发到对应的Target。
2.5 doesNotRecognizeSelector:抛出unrecognized selector sent to …异常。

Aspects大体流程

比如要hook的方法名为obtainImageName,如果是实例对象则动态创建一个子类(hook操作在子类操作),如果是类对象(通过该对象的isa的指向来判断是否是元类)或者被KVO过的对象,则不会创建子类,并把类中的forwardInvocation的IMP替换为__ASPECTS_ARE_BEING_CALLED__(如果被hook类中有forwardInvocation的实现,则会添加一个新方法Aspects_forwardInvocation,指向了原来的forwardInvocation,如果在__ASPECTS_ARE_BEING_CALLED__中不能处理,则执行该事件!)在子类或类对象中添加一个Aspects_obtainImageName的方法,然后将Aspects_obtainImageName的IMP指向原来的obtainImageName方法的IMP(保存原方法实现,后面要调用),再把被hook的方法obtainImageName的IMP指向_objc_msgForward,这样就进入了消息转发流程,而forwardInvocation的IMP被替换成了ASPECTS_ARE_BEING_CALLED,这样就会进入ASPECTS_ARE_BEING_CALLED进行拦截处理,当消息转发完成后,销毁hook逻辑,并将被hook类中的obtainImageName方法的IMP指回原IMP,删除别名SEL,整个流程结束!

3. Aspects 中4个基本类简要概述

4. 主要代码介绍

4.1 准备工作

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

    __block AspectIdentifier *identifier = nil;
    //  保护了block的线程安全 (使用了自旋锁)
    aspect_performLocked(^{
        //  判断hook方法合法性的代码
        if (aspect_isSelectorAllowedAndTrack(self, selector, options, error)) {
            // 获取或者创建AspectsContainer容器了 (给该类动态添加一个Aspect管理对象,并关联本类)
            AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);
            // 创建一个新的AspectIdentifier
            identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error];
            
            // 可能会创建失败,那就是aspect_isCompatibleBlockSignature方法返回NO。返回NO就意味着,我们要替换的方法block和要替换的原方法,两者的方法签名是不相符的。
            if (identifier) {
                
                // 完成了容器和AspectIdentifier初始化之后,就可以开始准备进行hook了。通过options选项分别添加到容器中的beforeAspects,insteadAspects,afterAspects这三个数组
                [aspectContainer addAspect:identifier withOptions:options];

               // 动态生成子类(带有_Aspects_后缀的子类),并交换类的原方法的实现为_objc_msgForward 使其直接进入消息转发模式
                aspect_prepareClassAndHookSelector(self, selector, error);
            }
        }
    });
    return identifier;
}
static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError **error) {
    NSCParameterAssert(selector);
    
    // 生成子类或者直接替换类方法
    Class klass = aspect_hookClass(self, error);
    
    // klass是我们hook完原始的class之后得到的类,可以从它这里获取到原有的selector的IMP
    Method targetMethod = class_getInstanceMethod(klass, selector);
    IMP targetMethodIMP = method_getImplementation(targetMethod);
    // 判断当前IMP是不是_objc_msgForward或者_objc_msgForward_stret,即判断当前IMP是不是消息转发
    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);
        // 如果该类不能响应aspects_xxxx
        if (![klass instancesRespondToSelector:aliasSelector]) {
            // 就为klass添加aspects_xxxx方法,方法的实现为原生方法的实现
            __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.
        // 交换类的原方法的实现为_objc_msgForward
        class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);
        AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
    }
}

4.2 消息转发逻辑处理

static void __ASPECTS_ARE_BEING_CALLED__(__unsafe_unretained NSObject *self, SEL selector, NSInvocation *invocation) {
    NSCParameterAssert(self);
    NSCParameterAssert(invocation);
    // 获取原始的selector
    SEL originalSelector = invocation.selector;
    // 获取带有aspects_xxxx前缀的方法
    SEL aliasSelector = aspect_aliasForSelector(invocation.selector);
    // 替换selector
    invocation.selector = aliasSelector;
    // 获取实例对象的容器objectContainer,这里是之前aspect_add关联过的对象。
    AspectsContainer *objectContainer = objc_getAssociatedObject(self, aliasSelector);
    // 获取获得类对象容器classContainer
    AspectsContainer *classContainer = aspect_getContainerForClass(object_getClass(self), aliasSelector);
    // 初始化AspectInfo,传入self(原始)、invocation(原始)参数
    AspectInfo *info = [[AspectInfo alloc] initWithInstance:self invocation:invocation];
    NSArray *aspectsToRemove = nil;

    // Before hooks.
    
    // 遍历beforeAspects中的hook,执行外部的block
    aspect_invoke(classContainer.beforeAspects, info);
    aspect_invoke(objectContainer.beforeAspects, info);

    // Instead hooks.
    // 这一段代码是实现Instead hooks的。先判断当前insteadAspects是否有数据,如果没有数据则判断当前继承链是否能响应aspects_xxx方法,如果能,则直接调用aliasSelector。注意:这里的aliasSelector是原方法method
    BOOL respondsToAlias = YES;
    // 判断如果是替换
    if (objectContainer.insteadAspects.count || classContainer.insteadAspects.count) {
        aspect_invoke(classContainer.insteadAspects, info);
        aspect_invoke(objectContainer.insteadAspects, info);
    }else {
        // 执行aliasSelector,回调原类中的的原方法
        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);
    
    // 提示: before、instead、after对应时间的Aspects切片的hook如果能被执行的,都执行完毕了。

    // If no hooks are installed, call original implementation (usually to throw an exception)
    // 如果aliasSelector无法响应(原类中方法没有实现),判断原类中是否定义了消息转发,如果有则调用原类中的消息转发(ForwardInvocation),如果没有抛出异常
    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];
        }
    }

    // Remove any hooks that are queued for deregistration.
    // 最后调用移除方法,移除hook。
    [aspectsToRemove makeObjectsPerformSelector:@selector(remove)];
}

//宏内容
#define aspect_invoke(aspects, info) \
for (AspectIdentifier *aspect in aspects) {\
    [aspect invokeWithInfo:info];\
    if (aspect.options & AspectOptionAutomaticRemoval) { \
        aspectsToRemove = [aspectsToRemove?:@[] arrayByAddingObject:aspect]; \
    } \
}

- (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.
    // 把AspectInfo存入到blockInvocation中,作为第一个参数
    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];
    }
    //执行block
    [blockInvocation invokeWithTarget:self.block];
    
    if (argBuf != NULL) {
        free(argBuf);
    }
    return YES;
}

4.3 销毁Aspect流程

static BOOL aspect_remove(AspectIdentifier *aspect, NSError **error) {
    NSCAssert([aspect isKindOfClass:AspectIdentifier.class], @"Must have correct type.");

    __block BOOL success = NO;
    aspect_performLocked(^{
        
        id self = aspect.object; // strongify
        if (self) {
            AspectsContainer *aspectContainer = aspect_getContainerForObject(self, aspect.selector);
            // 删除hook Block所有信息
            success = [aspectContainer removeAspect:aspect];
            
            // 移除之前hook的class和selector
            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_cleanupHookedClassAndSelector(NSObject *self, SEL selector) {
    NSCParameterAssert(self);
    NSCParameterAssert(selector);

    // klass是现在的class(原来的类),如果是元类,就转换成元类。
    Class klass = object_getClass(self);
    BOOL isMetaClass = class_isMetaClass(klass);
    if (isMetaClass) {
        klass = (Class)self;
    }

    
    // 获取原来类的方法的IMP是不是指向了_objc_msgForward,如果是,就把该方法的IMP再指回去
    // Check if the method is marked as forwarded and undo that.
    Method targetMethod = class_getInstanceMethod(klass, selector);
    IMP targetMethodIMP = method_getImplementation(targetMethod);
    if (aspect_isMsgForwardIMP(targetMethodIMP)) {
        // Restore the original method implementation.
        const char *typeEncoding = method_getTypeEncoding(targetMethod);
        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));
    }

    // Deregister global tracked selector
    // 移除AspectTracker里面所有标记的swizzledClassesDict。销毁全部记录的selector
    aspect_deregisterTrackedSelector(self, 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) {
        // Destroy the container
        // 需要还原类的AssociatedObject关联对象,以及用到的AspectsContainer容器。
        aspect_destroyContainerForObject(self, selector);

        // Figure out how the class was modified to undo the changes.
        NSString *className = NSStringFromClass(klass);
        if ([className hasSuffix:AspectsSubclassSuffix]) {
            // 把新建类的isa指针指向原来类
            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) {
                // 销毁了AspectsContainer容器,并且把关联对象也置成了nil
                aspect_undoSwizzleClassInPlace((Class)self);
            }else if (self.class != klass) {
                aspect_undoSwizzleClassInPlace(klass);
            }
        }
    }
}

5. 注意事项

最后附上一张流程图
流程图.png
上一篇下一篇

猜你喜欢

热点阅读