美文网首页iOS开发面试题iOS逆向工程
如何为一个实例动态替换方法

如何为一个实例动态替换方法

作者: Joy___ | 来源:发表于2017-03-20 15:48 被阅读1740次

这个 Tip 来源于一道面试题,感觉很是考察知识变通的能力,对 KVO 深入了解的同学,应该很容易就可以答出来。这里抛砖引玉,简单聊聊这个 Tip

首先简单总结下 KVO 的大概原理

  • 当你观察一个对象时,会动态的创建该对象类的子类,这个子类重写了被观察属性的 setter 方法,同时将该对象的 isa 指针指向了新创建的子类。在 Objective-C 中对象是通过 isa 指针来查找对应类中的方法列表的,所以这里可以把该对象看为新子类的实例对象。重写的 setter 方法会在调用原 setter 方法之后,通知观察者对象属性值的更改。

好的,下面进入正题,聊聊如何为一个实例动态替换方法。

首先创建一个初始化工程,直接对 ViewController 进行实战即可,在 ViewController 中加入一个 eat 方法,如下

- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor redColor];
}

- (void)eat {
    NSLog(@"original eat");
}

然后写一个 NSObject 的 Category 负责进行方法交换,将原对象的 isa 指针指向该对象类的子类(LDSubclass),并在子类中重写 eat 方法

@implementation NSObject (Hook)

+ (void)hookWithInstance:(id)instance method:(SEL)selector {
 
    Method originMethod = class_getInstanceMethod([instance class], selector);
    if (!originMethod) {
        // exception ..
    }
    
    Class newClass = [LDSubclass class];
    
    // 修改 isa 指针的指向
    object_setClass(instance, newClass);

}

@end

子类的代码很简单,就是重写 eat 方法,如果有需要,可以调用原方法的实现

@implementation LDSubclass

- (void)eat {
    NSLog(@"newSubClass eat");
    
    struct objc_super superClazz = {
        .receiver = self,
        .super_class = class_getSuperclass(object_getClass(self))
    };
    
    // 调用原方法
    void (*objc_msgSendSuperCasted)(void *, SEL) = (void *)objc_msgSendSuper;
    
    objc_msgSendSuperCasted(&superClazz, _cmd);
}
@end

最后在 ViewControlller 中进行测试即可,此时的 ViewController 代码如下

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor redColor];
    
    ViewController * vc = [[ViewController alloc] init];

    [vc eat];
    
    NSLog(@"-----------");
    
    ViewController * hookedInstance= [[ViewController alloc] init];
    
    [ViewController hookWithInstance:hookedInstance method:@selector(eat)];
    
    [hookedInstance eat];
}

- (void)eat {
    NSLog(@"original eat");
}
@end

来看看打印的结果,第一个没有 Hook 的实例,正常执行;第二个Hook 后的实例,先执行重写的方法,后执行原方法。

2017-03-20 14:30:21.244 JYHookInstanceMethod[91153:3422584] original eat
2017-03-20 14:30:21.244 JYHookInstanceMethod[91153:3422584] -----------
2017-03-20 14:30:21.245 JYHookInstanceMethod[91153:3422584] newSubClass eat
2017-03-20 14:30:21.245 JYHookInstanceMethod[91153:3422584] original eat

原理

与 KVO 的 isa-swizzling 思路相同,对想要 Hook 实例的类创建一个子类,并在子类中重写想要 Hook 的方法,将该实例的 isa 指针指向子类,这样进行方法查找时,便会在子类方法列表进行查找,如果想要执行更多操作,可以在替换后的新方法中加入自己的逻辑。

这里只是一个超级简单的 Demo,很多边界情况没有考虑,后期可以自己完善,Demo 可以参考JYHookInstanceMethod

Reference

相关文章

  • 如何为一个实例动态替换方法

    这个 Tip 来源于一道面试题,感觉很是考察知识变通的能力,对 KVO 深入了解的同学,应该很容易就可以答出来。这...

  • 如何为一个实例动态替换方法

    作者Joy___已关注 2017.03.20 15:48*字数 583阅读 78评论 4喜欢 5 这个 Tip 来...

  • 使用__slots__实现动态绑定

    Python作为动态语言,可以实现动态绑定属性和实例方法等。 动态绑定属性 动态绑定实例方法 给一个实例绑定的方法...

  • 为一个实例动态替换方法(demo)

    文章地址:http://goodrui.me/2017/06/29/%E4%B8%BA%E4%B8%80%E4%B...

  • iOS Runtime学习(三) -- 简单使用篇

    一、前言 runtime功能很强大,本文简单的介绍几个实用的小功能,如动态添加属性,动态添加方法,方法替换,字典转...

  • java为什么静态方法只能调用静态方法

    因为静态方法是属于类的,动态方法属于实例对象,动态方法只有在对象实例化之后才存在, 如果静态方法能调用动态方法的话...

  • iOS RunTime 理解

    可以遍历对象的属性 可以动态的添加、修改属性,动态添加、修改、替换方法,动态添加、修改、替换协议 可以动态创建类、...

  • runtime学习系列之方法调用

    苹果开源网站官方文档 在Objective-C里面,方法分为实例方法和动态方法,但是不管是实例方法还是动态方法,最...

  • Python动态绑定属性方法

    python是动态语言,可以为实例动态绑定属性、方法,也可以为类动态绑定方法。即在用到的时候定义。为实例动态绑定的...

  • runtime的使用二

    动态添加方法 替换方法 方法交换 获取成员变量,属性,以及方法

网友评论

  • 维维豆奶1991:struct objc_super superClazz = {
    .receiver = self,
    .super_class = class_getSuperclass(object_getClass(self))
    };

    // 调用原方法
    void (*objc_msgSendSuperCasted)(void *, SEL) = (void *)objc_msgSendSuper;

    objc_msgSendSuperCasted(&superClazz, _cmd);
    这段代码的意思是在子类去调用父类的setter 方法么?
  • Neo_joke:其实就是用runtime多做了一层继承,因为setclass是原类型的子类,所以这个继承关系是正确的,其实我们可以发散一下,这些动态的inspect技术究竟有什么实际场景?比方说笔者提到了KVO,那么这属于一个技术框架,对实例动态添加方法其实跟对类动态添加方法是一样的,再往本质上说一点就是不需要硬编码而实现修改继承关系,你可以多做一层继承或者少做一层继承,比方说可以实现一个ORM框架,所有的DataModel你都把它的元类给修改了,添加一些增删改查的方法,把属性都扫描一遍与数据库的表做映射,这样,这个ORM框架不需要Model去硬编码继承某个对象,就实现了增删改查,或者实现一个AOP框架,通过多增加一层继承,对方法都进行切片等等
  • SeaHub:膜!
    Joy___:@SeaHub 你够了:sweat:
  • 箪食豆羹:沙发!赞!

本文标题:如何为一个实例动态替换方法

本文链接:https://www.haomeiwen.com/subject/cfqinttx.html