前言
aspects 是ios中一款hook框架,使用方便,功能强大,是AOP编程中不可或缺的框架
其巧妙地利用Method-swizzle实现了以block的方式来hook指定函数,除了可以hook指定对象或类,还可以指定hook的方式
Aspects
Aspect新建的核心类主要关系如下所示,Aspects 作为 NSObject 的分类,协调调用其他的类以实现hook功能
使用简介
其分别提供了类方法和对象方法来 hook 对应的对象的实例方法或者类的实例方法(hook类方法就是hook对象元类的实例方法),hook之后会返回AspectToken,AspectToken对象可以用于在合适的位置移除hook
下面是声明以及使用的案例方法,看着很简洁
+ (id<AspectToken>)aspect_hookSelector:(SEL)selector
withOptions:(AspectOptions)options
usingBlock:(id)block
error:(NSError **)error;
- (id<AspectToken>)aspect_hookSelector:(SEL)selector
withOptions:(AspectOptions)options
usingBlock:(id)block
error:(NSError **)error;
//Aspect的使用,
//hook 实例对象的实例方法(仅仅hook该对象,不影响该实例对象所在类的其他实例对象)
id<AspectToken> aspect = [vc aspect_hookSelector:@selector(viewDidLoad:)
withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo){
} error:nil];
//hook 类的实例方法(即该类所有的对象都被hook)
id<AspectToken> aspect = [UIViewController aspect_hookSelector:@selector(viewDidLoad:)
withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo){
} error:nil];
//hook 元类的实例方法(即该类的类方法被hook)
id<AspectToken> aspect = [objc_getMetaClass("Person") aspect_hookSelector:@selector(logInfo)
withOptions:AspectPositionInstead usingBlock:^(id<AspectInfo> aspectInfo){
NSLog(@"ViewController哈哈哈");
} error:nil];
//移除hook代码
[aspect remove];
其中options枚举值关系着 hook方法执行的时机和方式,如下所示
typedef NS_OPTIONS(NSUInteger, AspectOptions) {
//在的hook方法之后执行
AspectPositionAfter = 0, /// Called after the original implementation (default)
//替换掉hook的方法
AspectPositionInstead = 1, /// Will replace the original implementation.
//在的hook方法之前执行
AspectPositionBefore = 2, /// Called before the original implementation.
//hook之后,执行一次自动移除,可以通过按位与的方式加入此功能
//AspectPositionAfter | AspectOptionAutomaticRemoval
AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution.
};
实现源码介绍
hook调用的入口方法就是 aspect_hookSelector,我们进入窥探究竟,此过程可以观察block是怎样一步一步被保存,最后在哪里调用的
执行外层hook方法,最后均会调用 aspect_add函数
aspect_add
此方法是hook初始化的核心实现方法,主要逻辑都在这里
- (id<AspectToken>)aspect_hookSelector:(SEL)selector
withOptions:(AspectOptions)options
usingBlock:(id)block
error:(NSError **)error {
return aspect_add(self, selector, options, block, error);
}
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(^{
//检查此次hook是否允许,且此过程引入了tracker、以便于后续校验等操作
if (aspect_isSelectorAllowedAndTrack(self, selector, options, error)) {
//获取此类的Container,避免重复创建集合,用于保存方法签名等信息类AspectIdentifier对象
AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);
//创建AspectIdentifier用于保存方法签名等信息
identifier = [AspectIdentifier identifierWithSelector:selector
object:self options:options block:block error:error];
if (identifier) {
//将签名信息放到集合Container中,以便于查找重复和调用
//Container与被绑的self关联,self为对象或者类对象
[aspectContainer addAspect:identifier withOptions:options];
// Modify the class to allow message interception.
//hook方法,实现block与原方法的imp逻辑处理(不是单纯的交换)
aspect_prepareClassAndHookSelector(self, selector, error);
}
}
});
return identifier;
}
可以看到上面的逻辑:
1、校验方法并引入tracker,用于更新后续校验等操作
2、一个类或者对象统一使用container,来管理AspectIdentifier方法签名等信息
3、根据block等创建方法签名等信息到AspectIdentifier类中
4、添加AspectIdentifier到container中
5、hook逻辑实现(实际交换利用了消息转发来处理回调)
而hook的逻辑则是在交换后的方法 __ASPECTS_ARE_BEING_CALLED__ 中处理,后续会介绍
aspect_isSelectorAllowedAndTrack
该方法是检查hook,且此过程引入了tracker,避免了重复的hook和其他操作
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;
}
//如果交换的是dealloc方法
// 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;
}
//查看该类中是能响应此方法
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;
}
// Search for the current class and the class hierarchy IF we are modifying a class object
//当前对象是否是类对象,即是否调用的类方法,或者类对象的实例方法(所有的实例对象都会调用),此情况不能重复hook
if (class_isMetaClass(object_getClass(self))) {
Class klass = [self class];
//单例字段,用于以class为键值,保存该类的tracker信息
NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict();
Class currentClass = [self class];
//获取tracker信息
AspectTracker *tracker = swizzledClassesDict[currentClass];
//如果子类已经有hook后的tracker信息,子类hook则不能在hook父类,不然会出现问题
if ([tracker subclassHasHookedSelectorName:selectorName]) {
//从tracker中子类查找是否已经被hook了,子类信息被保存在当前类对应的tracker中
NSSet *subclassTracker = [tracker subclassTrackersHookingSelectorName:selectorName];
NSSet *subclassNames = [subclassTracker valueForKey:@"trackedClassName"];
NSString *errorDescription = [NSString stringWithFormat:@"Error: %@ already hooked subclasses: %@. A method can only be hooked once per class hierarchy.", selectorName, subclassNames];
AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription);
return NO;
}
//利用循环开始查找此类的实际响应者,找到后返回YES
do {
tracker = swizzledClassesDict[currentClass];
if ([tracker.selectorNames containsObject:selectorName]) {
if (klass == currentClass) {
// Already modified and topmost!
return YES;
}
NSString *errorDescription = [NSString stringWithFormat:@"Error: %@ already hooked in %@. A method can only be hooked once per class hierarchy.", selectorName, NSStringFromClass(currentClass)];
AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription);
return NO;
}
} while ((currentClass = class_getSuperclass(currentClass)));
类中没找到该方法,说明该方法被修改过了
currentClass = klass;
AspectTracker *subclassTracker = nil;
//遍历当前类的父类,生成tracker,保存到单例集合中
//更新tracker的selector集合,且每个父类保存所有的子类tracker集合,以selectorName为键值保存
do {
tracker = swizzledClassesDict[currentClass];
if (!tracker) {
tracker = [[AspectTracker alloc] initWithTrackedClass:currentClass];
swizzledClassesDict[(id<NSCopying>)currentClass] = tracker;
}
if (subclassTracker) {
[tracker addSubclassTracker:subclassTracker hookingSelectorName:selectorName];
} else {
[tracker.selectorNames addObject:selectorName];
}
// All superclasses get marked as having a subclass that is modified.
subclassTracker = tracker;
}while ((currentClass = class_getSuperclass(currentClass)));
} else {
return YES;
}
return YES;
}
aspect_getContainerForObject
其为获取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;
}
AspectIdentifier初始化签名信息
AspectIdentifier类通过调用identifierWithSelector:selector object:self options:options block:block error:error]函数保存签名,将block保存下来,是非常重要的一点
其主要实现逻辑如下所示
+ (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError **)error {
NSCParameterAssert(block);
NSCParameterAssert(selector);
//获取block的签名信息
NSMethodSignature *blockSignature = aspect_blockMethodSignature(block, error);
//检查block的签名信息是否和hook的selector是否对应(肯定不是完全一致)
if (!aspect_isCompatibleBlockSignature(blockSignature, object, selector, error)) {
return nil;
}
//将保存的签名一起selector等信息保存到AspectIdentifier中去
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;
}
可以看到block的签名和校验过程是非常重要的,保存下来了,那么后面才可以在对应的地方回调,下面看看block的签名信息是怎么获得的,又如何校验签名信息的
block签名信息获取
block签名的获取是调用了aspect_blockMethodSignature方法实现
其中引入了block的内部结构,以便于从中获取block的函数签名信息
// Block internals.
typedef NS_OPTIONS(int, AspectBlockFlags) {
//当block有此参数时,会有void (*copy)(void *dst, const void *src);参数
AspectBlockFlagsHasCopyDisposeHelpers = (1 << 25),
//是否有签名信息,当有签名信息的block,才能有signatrue签名等信息
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;
//当block的枚举值为AspectBlockFlagsHasSignature时,下面会有函数签名等信息
// 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;
//获取函数签名信息
static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error) {
//将block强转成AspectBlockRef结构,可以从中获取到签名信息
AspectBlockRef layout = (__bridge void *)block;
//如果是AspectBlockFlagsHasSignature枚举类型,则可以获取函数签名信息,否则不能直接结束
if (!(layout->flags & AspectBlockFlagsHasSignature)) {
NSString *description = [NSString stringWithFormat:
@"The block %@ doesn't contain a type signature.", block];
AspectError(AspectErrorMissingBlockSignature, description);
return nil;
}
//通过上面的block结构信息,可以看到参数在block的descriptor参数中
//而descriptor参数中有两个unsigned long int类型,和一个void *类型参数,由于他们是顺序储存的
//则可以通过过移位的方式获取第四个参数signature
void *desc = layout->descriptor;
desc += 2 * sizeof(unsigned long int); //计算前两个参数大小,移位
if (layout->flags & AspectBlockFlagsHasCopyDisposeHelpers) {
//由于这个参数是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签名信息校验
前面获取到了block的签名信息,后面则需要通过签名信息和selector对应的签名信息进行对比,如果参数不一致,则不能进行hook,否则可以hook
即调用 aspect_isCompatibleBlockSignature方法进行检测签名信息,下面探究下怎么判断的
tatic BOOL aspect_isCompatibleBlockSignature(NSMethodSignature *blockSignature,
id object, SEL selector, NSError **error) {
NSCParameterAssert(blockSignature);
NSCParameterAssert(object);
NSCParameterAssert(selector);
BOOL signaturesMatch = YES;
//获取到对象hook的selector签名信息
NSMethodSignature *methodSignature = [[object class]
instanceMethodSignatureForSelector:selector];
//对比selector和block的签名信息来判断是否一致
//先对比签名数量,block的数量默认要比selector的少
//select默认参数为(id self, SEL _cmd, ...),签名信息为 v@: @和:表示两个参数self和_cmd
//block的默认参数则为(block, ...), 签名信息为v@? 其中@?是组合的表示一个
//因此block的默认参数比selector要少一个,所以追加了一个遵循<AspectInfo>协议的参数
//所以他们参数数量相等了
if (blockSignature.numberOfArguments > methodSignature.numberOfArguments) {
//block参数比selector多,不符合条件,无法替换
signaturesMatch = NO;
}else {
block的参数没有selector的多,属于则开始继续校验
if (blockSignature.numberOfArguments > 1) {
//先检查第block的第2个参数是否是id类型毕竟,第一个看到的参数为遵循<AspectInfo>协议的对象
const char *blockType = [blockSignature getArgumentTypeAtIndex:1];
if (blockType[0] != '@') {
signaturesMatch = NO;
}
}
// 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;
}
addAspect
将AspectIdentifier参数保存到container中,根据不同的类型将保存的block相关新保存到不同数组,以被交换的selector为基准,将block分别放到其之前、替换、之后,分为三个数组,一次保存
之前和之后则是不与原方法冲突,仅仅是追加方法,而替换方法,则不在调用原方法
- (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;
}
}
实现hook来实现函数交换等逻辑
Aspects中hook不是单纯的交换信息,前面提到了,通过调用aspect_prepareClassAndHookSelector函数来实现响应功能,如果熟悉KVO和消息发送,相信会一目了然
static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError **error) {
NSCParameterAssert(selector);
//获取用于hook生成的子类,名字前缀_Aspects_,是不是和 NSKVONotifying_ 很相似
Class klass = aspect_hookClass(self, error);
//获取被交换的selector的实际方法,如果没有或获取到父类的Method
Method targetMethod = class_getInstanceMethod(klass, selector);
//获取 selector的 IMP
IMP targetMethodIMP = method_getImplementation(targetMethod);
//检查该方法是否消息转发的imp -- _objc_msgForward, 如果是则不需要额外处理
if (!aspect_isMsgForwardIMP(targetMethodIMP)) {
// Make a method alias for the existing method implementation, it not already copied.
//获取selector的方法签名等信息
const char *typeEncoding = method_getTypeEncoding(targetMethod);
//SEL:aspect__logName --- (IMP)logName
SEL aliasSelector = aspect_aliasForSelector(selector);
//检查改了实例方法是否有该犯法
if (![klass instancesRespondToSelector:aliasSelector]) {
//如果没有则添加该方法到该子类中,避免交换了父类的方法
__unused BOOL addedAlias = class_addMethod(klass, aliasSelector, method_getImplementation(targetMethod), typeEncoding);
NSCAssert(addedAlias,
@"Original implementation for %@ is already copied to %@ on %@",
NSStringFromSelector(selector),
NSStringFromSelector(aliasSelector), klass);
}
// We use forwardInvocation to hook in.
//logName --- _objc_msgForward
//将该方法和 Aspects定义的方法进行交换
/即调用aspect_getMsgForwardIMP,将原方法调用改为_objc_msgForward,即应用消息转发
class_replaceMethod(klass, selector, a
spect_getMsgForwardIMP(self, selector), typeEncoding);
AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
}
}
上面的方法中提到了 aspect_hookClass方法,我们看是怎么生成hook需要的子类,并且其中做了什么,来应对imp交换为_objc_msgForward 操作
aspect_hookClass
aspect_hookClass为实现hook的方法,下面看看怎么做的
static Class aspect_hookClass(NSObject *self, NSError **error) {
NSCParameterAssert(self);
获取当前类的class(如果是子类可能会被重写)
Class statedClass = self.class;
//获取当前对象的所属类信息,如果已经是类对象,则获取到的是元类
Class baseClass = object_getClass(self);
NSString *className = NSStringFromClass(baseClass);
// Already subclassed
//检查当前类是否是已经创建hook之后的子类,如果是,直接返回子类信息即可
if ([className hasSuffix:AspectsSubclassSuffix]) {
return baseClass;
}else if (class_isMetaClass(baseClass)) {
//如果不是子类当前类是元类,则直接交换即可
//不需要额外生成子类,并且标记为已经hook(被添加到swizzledClasses中去了)
return aspect_swizzleClassInPlace((Class)self);
// Probably a KVO'ed class. Swizzle in place. Also swizzle meta classes in place.
}else if (statedClass != baseClass) {
//如果当前类和所属类baseClass不一致,说明已经生成了新的子类了,直接实现子类方法交换
return aspect_swizzleClassInPlace(baseClass);
}
//还没有生成子类,开始生成子类,在子类中处理信息
//如果交换的是一个实例对象,可以避免对其他实例对象造成影响
// Default case. Create dynamic subclass.
//生成子类的名字,一般父类名字前加上固定前缀
const char *subclassName = [className
stringByAppendingString:AspectsSubclassSuffix].UTF8String;
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:方法替换成新的消息转发名字
aspect_swizzleForwardInvocation(subclass);
//分别重写该类的class的实例方法和类方法直,均直接返回父类的class
aspect_hookedGetClass(subclass, statedClass);
aspect_hookedGetClass(object_getClass(subclass), statedClass);
//注册该子类
objc_registerClassPair(subclass);
}
//将当前类指向为新生成的子类
object_setClass(self, subclass);
return subclass;
}
aspect_hookedGetClass
aspect_hookedGetClass为普通的重写class的方法,如下所示
static void aspect_hookedGetClass(Class class, Class statedClass) {
NSCParameterAssert(class);
NSCParameterAssert(statedClass);
Method method = class_getInstanceMethod(class, @selector(class));
//生成新的imp返回老的class信息
IMP newIMP = imp_implementationWithBlock(^(id self) {
return statedClass;
});
//将子类的class实现改为新生成的
class_replaceMethod(class, @selector(class), newIMP, method_getTypeEncoding(method));
}
aspect_swizzleForwardInvocation
aspect_swizzleForwardInvocation为交换消息转发的方法,并生成一个新的方法,如下所示
其中__aspects_forwardInvocation被作为备用的消息转发方法
static NSString *const AspectsForwardInvocationSelectorName = @"__aspects_forwardInvocation:";
static void aspect_swizzleForwardInvocation(Class klass) {
NSCParameterAssert(klass);
// If there is no method, replace will act like class_addMethod.
//将子类的消息转发的方法 forwardInvocation: 替换为__ASPECTS_ARE_BEING_CALLED__实现
//后面的hook后的方法实现均在__ASPECTS_ARE_BEING_CALLED__函数中
IMP originalImplementation = class_replaceMethod(klass, @selector(forwardInvocation:),
(IMP)__ASPECTS_ARE_BEING_CALLED__, "v@:@");
if (originalImplementation) {
//替换完成之后,添加新生成的__aspects_forwardInvocation方法,实现和消息转发的一样
//留着作为备用消息转发方法功能
class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName),
originalImplementation, "v@:@");
}
AspectLog(@"Aspects: %@ is now aspect aware.", NSStringFromClass(klass));
}
ASPECTS_ARE_BEING_CALLED
__ASPECTS_ARE_BEING_CALLED__为消息转发是调用的回调方法(实际上就是调用hook后的方法,原方法的imp都被替换成了_objc_msgForward,即均会执行消息转发功能)
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;
//获取当前类的container和实例对象的objectContainer
//classContainer
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.
//执行该对象所有hook的指定方法,以及该hook对象的类的实例方法
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)));
}
//执行该对象所有hook的指定方法,以及该hook对象的类的实例方法
// 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)
//如果原方法执行消息转发,且子类其或者其子类没有实现消息转发方法
//则使用方法发备用的消息转发方法__aspects_forwardInvocation来执行
//如果备用的消息转发方法__aspects_forwardInvocation也没实现,则crash
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)];
}
aspect_invoke
为执行hooks的宏,通过遍历aspects中的AspectIdentifier来执行hook方法,如果设置了 AspectOptionAutomaticRemoval枚举,则执行完毕后删除该hook方法
for (AspectIdentifier *aspect in aspects) {\
[aspect invokeWithInfo:info];\
if (aspect.options & AspectOptionAutomaticRemoval) { \
aspectsToRemove = [aspectsToRemove?:@[] arrayByAddingObject:aspect]; \
} \
}
AspectToken的remove
实际调用的是aspect_remove方法,从container中移除hook
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];
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;
}
最后
总结一下aspects流程,其分为注册和响应
注册hooks
-
aspects检查是否满足hook条件,包括黑名单,以及类方法hook校验等
-
对block进行签名,利用block的本来结构,通过指针位移获取到签名信息signature,获取block签名后并与selector参数进行校验
-
将签名信息放入container中,以便于响应hooks时调用
-
注册hooks相关内容,通过模仿KVO实现,新注册一个子类,将子类的class指向当前类,对子类的class方法重写,将消息转发方法调用
forwardInvocation转交到__ASPECTS_ARE_BEING_CALLED__方法调用,且预留__aspects_forwardInvocation作为消息转发的备用方法
5.替换原方法为 _objc_msgForward,调用原方法时会自动执行消息转发流程
响应hooks
1.获取对象和类对象的container
2.一次响应 before、instance、after的hooks,如果替换的方法没有实现(不是hook),则去父类查找并执行正常的消息转发流程,如果父类中消息转发都没实现,则在子类执行备用的__aspects_forwardInvocation 消息转发,否则crash
使用
从使用上来说,可以hook某一个实例对象的实例方法、hook一个类的所有实例方法、hook类方法
hook一个实例的实例方法,则是调用该实例对象的aspects对象实例方法进行hook
hook一个类的所有实例方法,则是调用该类方法进行hook,或者类对象的实例方法进行hook
hook一个类的类方法,则是调用该类对象的元类的实例方法进行hook