内容简介:在通过这个函数首先把传进来的
在 Aspects
源码分析的第一篇文章中主要分析了为 hook
做的准备工作,接下来分析一下,当 selector
执行时是如何执行你自己添加的自定义 hook
事件的。
通过 hook
准备工作的处理后 ,外界调用的 hook selector
会直接进入消息转发执行到方法 forwardInvocation:
,然后此时 forwardInvocation:
方法的IMP是指向处理 hook
的函数 __ASPECTS_ARE_BEING_CALLED__
,这个函数也是整个 hook
事件的核心函数。代码实现如下
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]; //aliasSelector 已经在 aspect_prepareClassAndHookSelector 函数中替换为原来selector的实现 , 这里就是调回原方法的实现代码 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; 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)]; } 复制代码
这个函数首先把传进来的 NSInvocation
对象的 selector
赋值为 IMP
指向调用方法的原 IMP
的 aliasSelector
, 这样可以方便调用会原方法的IMP的实现。
获取hook事件容器
AspectsContainer *objectContainer = objc_getAssociatedObject(self, aliasSelector); AspectsContainer *classContainer = aspect_getContainerForClass(object_getClass(self), aliasSelector); AspectInfo *info = [[AspectInfo alloc] initWithInstance:self invocation:invocation]; 复制代码
这里是通过aliasSelector分别取出绑定在 hook对象 以及 hook class (hook对象的isa指针指向的Class)中对应的容器对象 AspectsContainer
, 并生成一个 AspectInfo
对象,用于封装执行方法及hook事件是所需的实参。接下来分别是遍历两个容器对象中的三个数组( beforeAspects 、insteadAspects 、afterAspects
)是否有 hook
的标识对象 AspectIdentifier
, 如果有的话就执行相应的 hook
事件。 insteadAspects
如果这个数组有对象存放,就说明原方法的实现被替换为执行 insteadAspects
里的 hook
事件了。
hook执行
//执行hook aspect_invoke(classContainer.beforeAspects, info); //hook执行的宏代码 #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 { //根据block得签名字符串 , 生成对应的消息调用对象。用来在设置完参数后调用block 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. //这里设置Block的 第一个参数为传进来的AspectInfo对象 , 第0位置的参数是Block本身 if (numberOfArguments > 1) { //有参数的话就吧第一个参数 设置为 AspectInfo , 第0位置是block本身。 /** 官方文档解析 : When the argument value is an object, pass a pointer to the variable (or memory) from which the object should be copied &info : info对象指针的地址 这样传参的目的是保证了,参数无论是普通类型参数还是对象都可以通过你传进来的指针,通过拷贝指针指向的内容来获取到 普通类型数据 或者 对象指针。 */ [blockInvocation setArgument:&info atIndex:1]; } void *argBuf = NULL; //遍历参数类型typeStr , 为blockInvocation对应的参数创建所需空间 , 赋值数据 , 设置blockInvocation参数 for (NSUInteger idx = 2; idx < numberOfArguments; idx++) { const char *type = [originalInvocation.methodSignature getArgumentTypeAtIndex:idx]; NSUInteger argSize; //实参多需要的空间大小 NSGetSizeAndAlignment(type, &argSize, NULL); //根据encodeType 字符串 创建对应空间存放block的参数数据所属要的size if (!(argBuf = reallocf(argBuf, argSize))) { //创建size大小的空间 AspectLogError(@"Failed to allocate memory for block invocation."); return NO; } [originalInvocation getArgument:argBuf atIndex:idx]; //获取到指向对应参数的指针 [blockInvocation setArgument:argBuf atIndex:idx]; //把指向对应实参指针的地址(相当于指向实参指针的指针)传给invocation 进行拷贝,得到的就是指向实参对象的指针 } [blockInvocation invokeWithTarget:self.block]; //设置完实参执行block if (argBuf != NULL) { free(argBuf); //c语言的创建空间 ,用完后需要释放,关于 c语言 的动态内存相关资料可以看 https://blog.csdn.net/qq_29924041/article/details/54897204 } return YES; } 复制代码
可以看出 AspectIdentifier
的 -invokeWithInfo
是执行 hook
事件最终的方法。该方法主要处理的事情是:根据传进来的 AspectInfo
对象为最初定义 hook
事件的 Block
设置相应的参数。并执行Block( hook
事件)
blockInvocation
设置参数解析
-
设置了
block
的第一个位置的参数为AspectInfo * info
, 这样做及未来方便内部遍历设置参数 (与selector保持一致,自定义参数从 索引为2的位置开始),又方便了外界在定义hook的事件是获取到实例对象 -[info instance]
-
getArgument:atIndex:
返回的是对应索引参数的指针(地址)。假如参数是一个对象指针的话,会返回对象的指针地址。而setArgument:atIndex:
会把传进来的参数(指针)拷贝其指向的内容到相应的索引位置中。所以argBuf
在整个for
循环中可以不断地使用同一个指针并不断的reallocf
返回指向一定堆空间的指针。argBuf
指针只是作为一个设置参数的中介,每一个for
循环后setArgument :atIndex:
都会把argBuf
指向的内容拷贝到invocation中。
hook的移除
AspectIdentifier
的 remove
方法,会调用到下面的函数
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); success = [aspectContainer removeAspect:aspect]; //重container的 三个数组中移除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; } 复制代码
1. 移除AspectContainer中的AspectIdentifier
首先获取被hook的对象中通过 runtime
绑定的关联属性 --- AspectsContainer *aspectContainer
,并分别移除数组的 hook标识对象 - AsepctIdentifier * aspect
,实现代码如下:
//AspectContainer的实例方法 - (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; } 复制代码
2.还原selector指向的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)); } 复制代码
在进行 hook
准备工作室,把 selector
的IMP修改成立进入消息转发的,并且添加了一个新的 selector
( asepct__selector
)指向原 selector
的 IMP
这里是还原selector的指向。
3.移除AspectTracker对应的记录
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))); } 复制代码
如果被 hook
的是类(调用的是类方法添加 hook
)。在全局对象(NSMutableDictionary *swizzledClassesDict)中移除 hook class
的整个向上继承关系链上的 AspectTracker
中的 selectors
数组中的 selectorName
字符串。
####4.还原被hook的实例对象的isa的指向 + 还原被hook Class的forwardInvocation:方法的IMP指向
// 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 aspect_destroyContainerForObject(self, selector); // Figure out how the class was modified to undo the changes. NSString *className = NSStringFromClass(klass); if ([className hasSuffix:AspectsSubclassSuffix]) { Class originalClass = NSClassFromString([className stringByReplacingOccurrencesOfString:AspectsSubclassSuffix withString:@""]); NSCAssert(originalClass != nil, @"Original class must exist"); object_setClass(self, originalClass); //把hook的类对象isa 从_Aspects_class -> 原来的类 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); } } } 复制代码
这里首先判断 hook
对象的 AspectContainer
属性数组中是否还有 AspectIndetafier
对象(hook标识)。如果没有的话就清除掉该对象的关联属性容器。接下来分两种情况进行还原处理
情况1. 被hook的是普通实例对象: 此时需要把对象的isa指向还原会为原来的Class --> object_setClass(self, originalClass);
情况2. 被hook的是类: 还原 forwardInvocation:
方法的IMP指向为原来的进入消息转发的IMP,并且从全局变量 swizzledClasses
中移除类名字符串的记录。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 曹工说Spring Boot源码(21)-- 为了让大家理解Spring Aop利器ProxyFactory,我已经拼了
- JMockit:单元测试利器
- 利器+
- Go 调试利器:delve
- go调试利器-delve
- 单元测试利器Mockito框架
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web信息架构(第3版)
Peter Morville、Louis Rosenfeld / 陈建勋 / 电子工业出版社 / 2008年8月 / 85.00
本书涵盖了信息架构基本原理和实践应用的方方面面。全书共7个部分,包括信息架构概述、信息架构的基本原理、信息架构的开发流程和方法论、信息架构实践、信息架构与组织、两个案例研究,以及参考资料清单。 本书兼具较高的理论价值和实用价值,曾被Web设计领域多本书籍重点推荐,是信息架构领域公认的经典书,不论新手还是专家都能各取所需。本书可供Web设计与开发者、Web架构师、网站管理者及信息管理相关人员参......一起来看看 《Web信息架构(第3版)》 这本书的介绍吧!