Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SDMagicHook与Aspects的异同 #3

Open
gsdios opened this issue Mar 3, 2020 · 3 comments
Open

SDMagicHook与Aspects的异同 #3

gsdios opened this issue Mar 3, 2020 · 3 comments

Comments

@gsdios
Copy link
Collaborator

gsdios commented Mar 3, 2020

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:方法传参即可,直观简便。

@Suerous
Copy link

Suerous commented Aug 9, 2020

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

@gsdios
Copy link
Collaborator Author

gsdios commented Aug 9, 2020

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
  1. 试下在Aspects的demo里面这样分别用系统的kov和aspects监听下setFrame方法会不会crash,认真跑下代码试下然后给下最新的结论
  2. 文章中出现过的词是原类(即原来的类)而非 元类(meta class),先把这两个概念搞清楚。具体什么问题导致不兼容可以先把demo运行起来跑一下看下实际情况再说。文章上已经讲的的很详细,对照着run一下?

@Suerous

@Suerous
Copy link

Suerous commented Aug 11, 2020

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
  1. 试下在Aspects的demo里面这样分别用系统的kov和aspects监听下setFrame方法会不会crash,认真跑下代码试下然后给下最新的结论
  2. 文章中出现过的词是原类(即原来的类)而非 元类(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
  1. 试下在Aspects的demo里面这样分别用系统的kov和aspects监听下setFrame方法会不会crash,认真跑下代码试下然后给下最新的结论
  2. 文章中出现过的词是原类(即原来的类)而非 元类(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就说不通了。这就是我没看懂过来问这个原类的问题

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants