两个问题
Aspects如何hook实例方法?
Aspects如何hook类的实例方法?
注:Aspects目前不支持hook类方法。
流程图
Aspects解答
- 替换对象的
forwardInvocation
方法为__ASPECTS_ARE_BEING_CALLED__
,该方法会在之前/替换/之后调用传入的 block。 - 把要hook的selector的imp替换成
_objc_msgForward
,直接走消息转发流程。
对象为实例
如果hook的对象为实例,则动态创建一个父类为实例父类的class,替换forwordInvocation
方法,重写该类和其元类的[xxx class]
方法返回实例原本的父类。然后把实例的isa指向该类。(是不是有点像kvo?)
对象为类
如果hook的对象为类,则替换forwordInvocation
方法。
注意
如果对象是kvo对象,也只替换forwordInvocation
方法。
疑问
消息转发如果在前几步被拦截了会怎样?
如果在resolveInstanceMethod
阶段被拦截,即实现了resolveInstanceMethod
方法,能够调用hook的方法;如果在forwardingTargetForSelector
阶段被拦截,不会调用hook方法。
resolveInstanceMethod
阶段
@implementation ViewController
#pragma clang diagnostic ignored "-Wundeclared-selector"
- (void)viewDidLoad {
[super viewDidLoad];
// hook method
[self aspect_hookSelector:@selector(nonExistMethod) withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> info) {
NSLog(@"Hook nonExistMethod using before mode");
} error:nil];
// call a non-exist method
[self performSelector:@selector(nonExistMethod)];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(nonExistMethod)) {
class_addMethod([self class], sel, (IMP)nonExistMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void nonExistMethodIMP(id self, SEL selector) {
NSLog(@"IMP for nonExistMethod");
}
#pragma clang diagnostic pop
@end
AspectsDemo[89062:2505035] Hook nonExistMethod using before mode
AspectsDemo[89062:2505035] IMP for nonExistMethod
forwardingTargetForSelector
阶段
@implementation ViewController
#pragma clang diagnostic ignored "-Wundeclared-selector"
- (void)viewDidLoad {
[super viewDidLoad];
// hook method
[self aspect_hookSelector:@selector(nonExistMethod) withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> info) {
NSLog(@"Hook nonExistMethod using before mode");
} error:nil];
// call a non-exist method
[self performSelector:@selector(nonExistMethod)];
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(nonExistMethod)) {
return [FowardObject new];
}
return [super forwardingTargetForSelector:aSelector];
}
#pragma clang diagnostic pop
@end
AspectsDemo[89132:2506990] Aspects: Unable to find selector -[ViewController nonExistMethod].
AspectsDemo[89132:2506990] IMP for nonExistMethod
如果已经实现了forwardInvocation会怎样?
不会走自己的forwardInvocation
方法。
Hook两个不同实例的相同selector会怎样?
不会相互影响。
@implementation FowardObject
- (void)doSomething {
NSLog(@"%@:doSomething", self);
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
FowardObject *obj1 = [FowardObject new];
[obj1 aspect_hookSelector:@selector(doSomething) withOptions:AspectPositionInstead usingBlock:^(id<AspectInfo> info) {
NSLog(@"Hook obj1(%@):doSomething using instead mode", obj1);
} error:nil];
FowardObject *obj2 = [FowardObject new];
[obj2 aspect_hookSelector:@selector(doSomething) withOptions:AspectPositionInstead usingBlock:^(id<AspectInfo> info) {
NSLog(@"Hook obj2(%@):doSomething using instead mode", obj2);
} error:nil];
[obj1 doSomething];
[obj2 doSomething];
[obj1 doSomething];
}
@end
AspectsDemo[94369:2658642] Hook obj1(<FowardObject_Aspects_: 0x6000011a9080>):doSomething using instead mode
AspectsDemo[94369:2658642] Hook obj2(<FowardObject_Aspects_: 0x6000011b3450>):doSomething using instead mode
AspectsDemo[94369:2658642] Hook obj1(<FowardObject_Aspects_: 0x6000011a9080>):doSomething using instead mode
补充
AspectTracker
作用:追踪每个类hook的selector情况,确保一条继承链上只有一个类hook了这个方法。
aspect_getSwizzledClassesDict
方法里保存了一个字典static NSMutableDictionary *swizzledClassesDict
,key为class,value为AspectTracker。用于aspect_isSelectorAllowedAndTrack
&& aspect_deregisterTrackedSelector
方法。
AspectIdentifier & AspectsContainer
AspectIdentifier<AspectToken>: Tracks a single aspect.
AspectsContainer:AspectIdentifier的容器,保存之前/替换/之后hook方法对应的AspectIdentifier。
相关方法:aspect_getContainerForObject
,aspect_getContainerForClass
,aspect_destroyContainerForObject
__ASPECTS_ARE_BEING_CALLED__
方法里调用对象的AspectsContainer 里保存的AspectIdentifier,调用hook方法。
aspect_cleanupHookedClassAndSelector
方法里记录对象是否还有hook方法,如果没有则清理对象。
源码结构
Aspects.png参考
ps:本文是个人看Aspects源码的理解,如果有不当之处麻烦各位指正。
网友评论