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

作者Joy___已关注

2017.03.20 15:48*字数 583阅读 78评论 4喜欢 5

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

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

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

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

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

- (void)viewDidLoad {    [superviewDidLoad];self.view.backgroundColor = [UIColorredColor];}- (void)eat {NSLog(@"original eat");}

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

@implementationNSObject (Hook)+ (void)hookWithInstance:(id)instancemethod:(SEL)selector{

Method originMethod = class_getInstanceMethod([instance class], selector);

if (!originMethod) {

// exception ..

}ClassnewClass = [LDSubclassclass];// 修改 isa 指针的指向object_setClass(instance, newClass);}@end

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

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

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

@implementationViewController- (void)viewDidLoad {    [superviewDidLoad];self.view.backgroundColor = [UIColorredColor];    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-2014:30:21.244JYHookInstanceMethod[91153:3422584]originaleat2017-03-2014:30:21.244JYHookInstanceMethod[91153:3422584]-----------2017-03-2014:30:21.245JYHookInstanceMethod[91153:3422584]newSubClasseat2017-03-2014:30:21.245JYHookInstanceMethod[91153:3422584]originaleat

原理

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 这个 Tip 来源于一道面试题,感觉很是考察知识变通的能力,对 KVO 深入了解的同学,应该很容易就可以答出来。这...
    Joy___阅读 7,967评论 6 32
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 5,918评论 0 9
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,487评论 30 472
  • 一、深复制和浅复制的区别? 1、浅复制:只是复制了指向对象的指针,即两个指针指向同一块内存单元!而不复制指向对象的...
    iOS_Alex阅读 5,321评论 1 27
  • 昨天晚上儿子打电话回来,声音听起来已经不是原来那么稚嫩了,问他这学期感觉怎么样?他说很好,说自己感冒了,老师有帮他...
    心若止水858阅读 1,729评论 0 0

友情链接更多精彩内容