Objective-C Runtime (三):Method Swizzling(方法替换)

Method Swizzling是一种改变改变一个'selector'的实际实现的技术。通过这一技术,我们可以在运行时通过修改类的分发表中selector对应的函数,来修改方法的实现。 实现图解如下图:

从上图中,我们可以看到,使用Method Swizzling本质上是将selectorC的方法实现IMPcselectorN的方法实现IMPn交换了,当我们调用selectorC,也就是给对象发送selectorC消息时,所查找到的对应的方法实现就是IMPn而不是IMPc了。

Method Swizzling在什么情况下可以用到了? 例如:我们接到一个需求:对 App 的用户行为进行追踪和分析。简单来说,就是,就是当用户进入某个界面或者点击某个按钮时,记录这个事件。

最粗暴的方式,就是在每个 viewDidAppear 里添加记录事件的代码。这种方式缺点是很明显的,它破坏了代码的干净整洁。因为记录事件的代码本身不属于原有代码的主要逻辑。随着项目扩大、代码增加,我们的原有代码里会到处分布着记录事件的代码。这时,要找到一段事件记录的代码会变得困难,也很容易忘记添加事件记录的代码。

我们可能会想到使用继承或类别,在重写的方法里添加事件记录的代码。但这样也会带来新的问题:

  1. 我们无法控制别人如何去实例化我们的子类;
  2. 对于类别,我们没办法调用到原来的方法实现。大多时候,我们重写一个方法只是为了添加一些代码,而不是完全取代它;
  3. 如果有两个类别都实现了相同的方法,运行时没法保证哪一个类别的方法会给调用;
  4. 每个 ViewController 里的 ButtonClick 方法命名不可能都一样。

我了解决以上的问题,我们可以使用Method Swizzling,如以下代码所示:

#import "UIViewController+Tracking.h"
#import <objc/runtime.h>
@implementation UIViewController (Tracking)+ (void)load {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{Class class = [self class];// When swizzling a class method, use the following:// Class class = object_getClass((id)self);SEL originalSelector = @selector(viewWillAppear:);SEL swizzledSelector = @selector(track_viewWillAppear:);Method originalMethod = class_getInstanceMethod(class, originalSelector);Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);BOOL didAddMethod =class_addMethod(class,originalSelector,method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod));if (didAddMethod) {class_replaceMethod(class,swizzledSelector,method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod));} else {method_exchangeImplementations(originalMethod, swizzledMethod);}});}- (void)track_viewWillAppear:(BOOL)animated {[self track_viewWillAppear:animated];NSLog(@"viewWillAppear: %@", self);
}@end
复制代码

从上面代码可以看出,我们通过method swizzling修改了UIViewController@selector(viewWillAppear:)对应的函数指针,使其实现指向了我们自定义的track_viewWillAppear:的实现。这样,当UIViewController及其子类的对象调用viewWillAppear时,都会打印一条日志信息。

上面代码需要解释的问题: class_addMethod:要先尝试添加原 selector 是为了做一层保护,因为如果这个类没有实现 originalSelector ,但其父类实现了,那 class_getInstanceMethod 会返回父类的方法。这样 method_exchangeImplementations 替换的是父类的那个方法,这当然不是你想要的。所以我们先尝试添加 orginalSelector ,如果已经存在,再用 method_exchangeImplementations 把原方法的实现跟新的方法实现给交换掉。

注意事项 Swizzling通常被称作是一种黑魔法,容易产生不可预知的行为和无法预见的后果。滥用可能会造成很多问题,如果遵从以下几点预防措施的话,还是比较安全的:

  1. Swizzling应该总是在+load中执行;
  2. Swizzling应该总是在dispatch_once中执行;
  3. 总是调用方法的原始实现(除非有更好的理由不这么做):API提供了一个输入与输出约定,但其内部实现是一个黑盒。Swizzle一个方法而不调用原始实现可能会打破私有状态底层操作,从而影响到程序的其它部分;
  4. 避免冲突:给自定义的分类方法加前缀,从而使其与所依赖的代码库不会存在命名冲突。

参考:

  1. nshipster.com/method-swiz…

Objective-C Runtime (三):Method Swizzling(方法替换)相关推荐

  1. 【原】iOS动态性(三) Method Swizzling以及AOP编程:在运行时进行代码注入

    概述 今天我们主要讨论iOS runtime中的一种黑色技术,称为Method Swizzling.字面上理解Method Swizzling可能比较晦涩难懂,毕竟不是中文,不过你可以理解为" ...

  2. runtime(四) method swizzling 与AOP编程

    什么是 AOP : (site: baike.baidu.com),引用百度百科中的解释就是: 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过 ...

  3. iOS 开发:『Runtime』详解(二)Method Swizzling

    本文用来介绍 iOS 开发中『Runtime』中的黑魔法Method Swizzling. 通过本文,您将了解到: Method Swizzling(动态方法交换)简介 Method Swizzlin ...

  4. iOS总结-Runtime篇之黑魔法Method Swizzling的滥用会有危险吗

    参考https://www.jianshu.com/p/19c5736c5d9a, http://blog.sina.com.cn/s/blog_a343f32b0101en4o.html runti ...

  5. Method Swizzling的各种姿势

    因为Objective-C的runtime机制, Method Swizzling这个黑魔法解决了我们实际开发中诸多常规手段所无法解决的问题, 比如代码的插桩,Hook,Patch等等. 我们首先看看 ...

  6. 本文将要讨论Objective-C中的方法替换(method replacement)和swizzling(移魂大法)。

    重写类的方法(Overriding Methods) Overriding methods在任何面向对象语言中都很常见,主要用于子类化中.在子类中复写一个方法,然后在子类的实例就可以使用这个被重写的方 ...

  7. runtime实践之Method Swizzling

    利用 Objective-C 的 Runtime 特性,我们可以给语言做扩展,帮助解决项目开发中的一些设计和技术问题.这一篇,我们来探索一些利用 Objective-C Runtime 的黑色技巧.这 ...

  8. Objective-C Runtime 运行时之四:Method Swizzling

    理解Method Swizzling是学习runtime机制的一个很好的机会.在此不多做整理,仅翻译由Mattt Thompson发表于nshipster的Method Swizzling一文. Me ...

  9. iOS总结-Runtime篇之用途及面试题的总结一黑魔法Method Swizzling

    runtime的用途基本分下面几类: 实现多继承Multiple Inheritance 上一篇里面的最后利用methodSignatureForSelector来进行一次转发,在forwardInv ...

最新文章

  1. torch 和torchvision对应关系并附下载路径
  2. 人与动物之间有没有一条不可逾越的鸿沟?——从基础存在论到生命哲学
  3. 洛谷P2178 品酒大会
  4. HDLBits 系列(18) BCD码计数器的设计
  5. GRE作文用AI打分,已经20周年了:AI给中国考生的分数,远高于人类打分
  6. 计算机5800怎么开机,神舟5800笔记本怎么进bios
  7. mysql explain的使用(优化查询)
  8. python中的拷贝
  9. java当中有关循环的代码_有关Java循环的内容,编程中还是比较常用的,下面分享给大家几个循环的示例代码,练习一下。1、循环输出1到100之间所有能被3或能被4整除的数。pack...
  10. matlab调用sh函数,Shell 函数定义与调用
  11. ASP.NET关于WebPages的一点总结
  12. 一份超详细的MySQL高性能优化实战总结!
  13. webservice studio 参数是DataSet时不支持中文 解决方法
  14. 第 14 章 垃圾回收概述
  15. JavaScript函数—JavaScript闭包
  16. 网易云音乐云盘上传歌词的方法
  17. java程序的编程规范
  18. 一米村长讲故事机器人_又一位主持人跑去给孩子讲故事,村长李锐推出有声故事品牌“村长讲故事”...
  19. android 加固崩溃,360加固后,夜神模拟器安装崩溃
  20. Oracle sql 按指定顺序产生序列号

热门文章

  1. 32. 脱壳篇-简单带壳的程序、反调试带壳的程序(堆栈平衡原理找OEP、代码段设置断点)
  2. MySql 5.6.36 64位绿色版安装
  3. 深入了解Debug和Release的区别
  4. json数组传递到后台controller
  5. AJAX异步检查,检查用户名是否存在
  6. java.net.ConnectException: Connection refused: connect
  7. Android Eclipse ADT使用Tips
  8. SharePoint 2013中修改windows 活动目录(AD)域用户密码的WebPart(免费下载)
  9. DP-访问者模式(Visitor Pattern)
  10. sourceforge.net cvs sample