SDMagicHook
SDMagicHook copied to clipboard
SDMagicHook与Aspects的异同
aspects和SDMagicHook基本思路都是基于类似kvo的isa替换,但是从api设计以及实现上也有明显的区别,我们通过以下示例简要介绍下:
1.解决了Aspects未能解决的KVO兼容问题,详见 https://mp.weixin.qq.com/s?__biz=MzI1MzYzMjE0MQ==&mid=2247486231&idx=1&sn=1c6584e9dcc3edf71c42cf396bcab051&chksm=e9d0c0f5dea749e34bf23de8259cbc7c868d3c8a6fc56c4366412dfb03eac8f037ee1d8668a1&token=1383088962&lang=zh_CN#rd
2.设计实现了一套更为高效灵活的API,举例如下 假设有这样一个自定义类Test,在其内部定义了一个求和的方法,接收四个int类型的参数。
@implementation Test
- (int)sumWithA:(int)a b:(int)b c:(int)c d:(int)d {
return a + b + c + d;
}
@end
现在要求将四个参数分别平方然后再求和。
使用aspects实现如下:
Test *testObj = [Test new];
[testObj aspect_hookSelector:@selector(sumWithA:b:c:d:) withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> info, int a, int b, int c, int d) {
int aa = a * a;
int bb = b * b;
int cc = c * c;
int dd = d * d;
[info.originalInvocation setArgument:&aa atIndex:2];
[info.originalInvocation setArgument:&bb atIndex:3];
[info.originalInvocation setArgument:&cc atIndex:4];
[info.originalInvocation setArgument:&dd atIndex:5];
} error:NULL];
int sum = [testObj sumWithA:1 b:2 c:3 d:4];
NSLog(@">>>> %d", sum); // >>>> 30
使用SDMagicHook实现如下:
Test *testObj = [Test new];
[testObj hookMethod:@selector(sumWithA:b:c:d:) impBlock:^(typeof(testObj) this, int a, int b, int c, int d) {
__block int res;
[this callOriginalMethodInBlock:^{
res = [this sumWithA:a * a b:b * b c:c * c d:d * d];
}];
return res;
}];
int sum = [testObj sumWithA:1 b:2 c:3 d:4];
NSLog(@">>>> %d", sum); // >>>> 30
由以上demo可以看出: 1.aspects使用AspectOptions来决定自定义方法和原始方法的执行顺序;SDMagicHook使用callOriginalMethodInBlock来调用原始方法,可以将原始方法放在自定义逻辑的前、中、后任意位置执行,更加灵活方便。
2.aspects将原始方法封装在NSInvocation里面,如果想要修改sumWithA:b:c:d:
的参数值需要调用setArgument:atIndex:方法来实现,api不够简洁友好;SDMagicHook只需在callOriginalMethodInBlock的block参数内部直接调用原始的sumWithA:b:c:d:
方法传参即可,直观简便。
1.Aspects在你说的那三个问题中是不存在的。实验了一下这三种情况,KVO和Aspects可以工作良好。虽然不知道readme是因为忘记更新了,还是说有其他的问题存在(不是这其中的三个问题)
2.还有就是文章说到KVO是获取原类的。那解释第三个问题是因为isa被重新覆盖,那第一个同样的也会有isa被覆盖。KVO应该是获取isa对应的类,而不是class方法对应的原类。所以第三个问题应该是因为类继承关系混乱了,因为KVO的类只会生成一次。KVO__A----->KVO__A__Custom----->KVO__A--->A,猜测类继承冲突,系统直接是KVO__A--->A,对象a是KVO__A的实例,所以失效 1.先调用 custom-KVO 再调用 native-KVO,native-KVO 和 custom-KVO 都运行正常 2.先调用 native-KVO 再调用 custom-KVO,custom-KVO 运行正常,native-KVO 会 crash 3.先调用 native-KVO 再调用 custom-KVO 再调用 native-KVO,native-KVO 运行正常,custom-KVO 失效,无 crash
1.Aspects在你说的那三个问题中是不存在的。实验了一下这三种情况,KVO和Aspects可以工作良好。虽然不知道readme是因为忘记更新了,还是说有其他的问题存在(不是这其中的三个问题)
2.还有就是文章说到KVO是获取原类的。那解释第三个问题是因为isa被重新覆盖,那第一个同样的也会有isa被覆盖。KVO应该是获取isa对应的类,而不是class方法对应的原类。所以第三个问题应该是因为类继承关系混乱了,因为KVO的类只会生成一次。KVO__A----->KVO__A__Custom----->KVO__A--->A,猜测类继承冲突,系统直接是KVO__A--->A,对象a是KVO__A的实例,所以失效 1.先调用 custom-KVO 再调用 native-KVO,native-KVO 和 custom-KVO 都运行正常 2.先调用 native-KVO 再调用 custom-KVO,custom-KVO 运行正常,native-KVO 会 crash 3.先调用 native-KVO 再调用 custom-KVO 再调用 native-KVO,native-KVO 运行正常,custom-KVO 失效,无 crash
//
// AspectsViewController.m
// AspectsDemo
//
// Created by Peter Steinberger on 05/05/14.
// Copyright (c) 2014 PSPDFKit GmbH. All rights reserved.
//
#import "AspectsViewController.h"
#import "Aspects.h"
@implementation AspectsViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];
[self.view aspect_hookSelector:@selector(setFrame:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info, CGRect frame){
NSLog(@"%@", [NSValue valueWithCGRect:frame]);
} error:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"%@", change);
}
@end
- 试下在Aspects的demo里面这样分别用系统的kov和aspects监听下setFrame方法会不会crash,认真跑下代码试下然后给下最新的结论
- 文章中出现过的词是原类(即原来的类)而非 元类(meta class),先把这两个概念搞清楚。具体什么问题导致不兼容可以先把demo运行起来跑一下看下实际情况再说。文章上已经讲的的很详细,对照着run一下?
@Suerous
1.Aspects在你说的那三个问题中是不存在的。实验了一下这三种情况,KVO和Aspects可以工作良好。虽然不知道readme是因为忘记更新了,还是说有其他的问题存在(不是这其中的三个问题) 2.还有就是文章说到KVO是获取原类的。那解释第三个问题是因为isa被重新覆盖,那第一个同样的也会有isa被覆盖。KVO应该是获取isa对应的类,而不是class方法对应的原类。所以第三个问题应该是因为类继承关系混乱了,因为KVO的类只会生成一次。KVO__A----->KVO__A__Custom----->KVO__A--->A,猜测类继承冲突,系统直接是KVO__A--->A,对象a是KVO__A的实例,所以失效 1.先调用 custom-KVO 再调用 native-KVO,native-KVO 和 custom-KVO 都运行正常 2.先调用 native-KVO 再调用 custom-KVO,custom-KVO 运行正常,native-KVO 会 crash 3.先调用 native-KVO 再调用 custom-KVO 再调用 native-KVO,native-KVO 运行正常,custom-KVO 失效,无 crash
// // AspectsViewController.m // AspectsDemo // // Created by Peter Steinberger on 05/05/14. // Copyright (c) 2014 PSPDFKit GmbH. All rights reserved. // #import "AspectsViewController.h" #import "Aspects.h" @implementation AspectsViewController - (void)viewDidLoad { [super viewDidLoad]; [self.view addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil]; [self.view aspect_hookSelector:@selector(setFrame:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info, CGRect frame){ NSLog(@"%@", [NSValue valueWithCGRect:frame]); } error:nil]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { NSLog(@"%@", change); } @end
- 试下在Aspects的demo里面这样分别用系统的kov和aspects监听下setFrame方法会不会crash,认真跑下代码试下然后给下最新的结论
- 文章中出现过的词是原类(即原来的类)而非 元类(meta class),先把这两个概念搞清楚。具体什么问题导致不兼容可以先把demo运行起来跑一下看下实际情况再说。文章上已经讲的的很详细,对照着run一下?
@Suerous
1.Aspects在你说的那三个问题中是不存在的。实验了一下这三种情况,KVO和Aspects可以工作良好。虽然不知道readme是因为忘记更新了,还是说有其他的问题存在(不是这其中的三个问题) 2.还有就是文章说到KVO是获取原类的。那解释第三个问题是因为isa被重新覆盖,那第一个同样的也会有isa被覆盖。KVO应该是获取isa对应的类,而不是class方法对应的原类。所以第三个问题应该是因为类继承关系混乱了,因为KVO的类只会生成一次。KVO__A----->KVO__A__Custom----->KVO__A--->A,猜测类继承冲突,系统直接是KVO__A--->A,对象a是KVO__A的实例,所以失效 1.先调用 custom-KVO 再调用 native-KVO,native-KVO 和 custom-KVO 都运行正常 2.先调用 native-KVO 再调用 custom-KVO,custom-KVO 运行正常,native-KVO 会 crash 3.先调用 native-KVO 再调用 custom-KVO 再调用 native-KVO,native-KVO 运行正常,custom-KVO 失效,无 crash
// // AspectsViewController.m // AspectsDemo // // Created by Peter Steinberger on 05/05/14. // Copyright (c) 2014 PSPDFKit GmbH. All rights reserved. // #import "AspectsViewController.h" #import "Aspects.h" @implementation AspectsViewController - (void)viewDidLoad { [super viewDidLoad]; [self.view addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil]; [self.view aspect_hookSelector:@selector(setFrame:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info, CGRect frame){ NSLog(@"%@", [NSValue valueWithCGRect:frame]); } error:nil]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { NSLog(@"%@", change); } @end
- 试下在Aspects的demo里面这样分别用系统的kov和aspects监听下setFrame方法会不会crash,认真跑下代码试下然后给下最新的结论
- 文章中出现过的词是原类(即原来的类)而非 元类(meta class),先把这两个概念搞清楚。具体什么问题导致不兼容可以先把demo运行起来跑一下看下实际情况再说。文章上已经讲的的很详细,对照着run一下?
@Suerous
1.按照你跑的代码确实是崩溃了。所以我也在问是不是其他的情况,我看你的demo写的num属性。所以我是用的name属性测试的这三种情况,是运行成功的。 [aspectsController addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; aspectsController.name = @"1"; [AspectsViewController aspect_hookSelector:@selector(setName:) withOptions:0 usingBlock:^(id info, NSString *name) { NSLog(@"2aspect hook----%@", name); } error:nil];
2.我并没有说meta class的问题,特定对象的hook和meta class也扯不上啊,我说的就是你文章说的原类(原来的类)。我还特地写了类的继承关系。我说的是KVO是获取对象的isa生成子类的,而不是原来的类。所以我才写了类的继承关系。现在我大概明白了,nativeKVO是先看class方法类对象有没有被创建过,没有就通过对象的isa生成子类,有的话,直接用。这样情况1就能说通了,不会和情况3解释有冲突了。我一直以为你说的是KVO一直按class方法(原来的类)去获取/创建子类,这样情况1就说不通了。这就是我没看懂过来问这个原类的问题