Objective-C Runtime (三):Method Swizzling(方法替换)
Objective-C Runtime (三):Method Swizzling(方法替换)
Method Swizzling
是一种改变改变一个'selector'的实际实现的技术。通过这一技术,我们可以在运行时通过修改类的分发表中selector对应的函数,来修改方法的实现。 实现图解如下图:
从上图中,我们可以看到,使用Method Swizzling
本质上是将selectorC
的方法实现IMPc
与selectorN
的方法实现IMPn
交换了,当我们调用selectorC
,也就是给对象发送selectorC
消息时,所查找到的对应的方法实现就是IMPn
而不是IMPc
了。
那Method Swizzling
在什么情况下可以用到了? 例如:我们接到一个需求:对 App 的用户行为进行追踪和分析。简单来说,就是,就是当用户进入某个界面或者点击某个按钮时,记录这个事件。
最粗暴的方式,就是在每个 viewDidAppear
里添加记录事件的代码。这种方式缺点是很明显的,它破坏了代码的干净整洁。因为记录事件
的代码本身不属于原有代码的主要逻辑。随着项目扩大、代码增加,我们的原有代码里会到处分布着记录事件
的代码。这时,要找到一段事件记录的代码会变得困难,也很容易忘记添加事件记录的代码。
我们可能会想到使用继承或类别,在重写的方法里添加事件记录的代码。但这样也会带来新的问题:
- 我们无法控制别人如何去实例化我们的子类;
- 对于类别,我们没办法调用到原来的方法实现。大多时候,我们重写一个方法只是为了添加一些代码,而不是完全取代它;
- 如果有两个类别都实现了相同的方法,运行时没法保证哪一个类别的方法会给调用;
- 每个 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通常被称作是一种黑魔法,容易产生不可预知的行为和无法预见的后果。滥用可能会造成很多问题,如果遵从以下几点预防措施的话,还是比较安全的:
- Swizzling应该总是在+load中执行;
- Swizzling应该总是在dispatch_once中执行;
- 总是调用方法的原始实现(除非有更好的理由不这么做):API提供了一个输入与输出约定,但其内部实现是一个黑盒。Swizzle一个方法而不调用原始实现可能会打破私有状态底层操作,从而影响到程序的其它部分;
- 避免冲突:给自定义的分类方法加前缀,从而使其与所依赖的代码库不会存在命名冲突。
参考:
- nshipster.com/method-swiz…
Objective-C Runtime (三):Method Swizzling(方法替换)相关推荐
- 【原】iOS动态性(三) Method Swizzling以及AOP编程:在运行时进行代码注入
概述 今天我们主要讨论iOS runtime中的一种黑色技术,称为Method Swizzling.字面上理解Method Swizzling可能比较晦涩难懂,毕竟不是中文,不过你可以理解为" ...
- runtime(四) method swizzling 与AOP编程
什么是 AOP : (site: baike.baidu.com),引用百度百科中的解释就是: 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过 ...
- iOS 开发:『Runtime』详解(二)Method Swizzling
本文用来介绍 iOS 开发中『Runtime』中的黑魔法Method Swizzling. 通过本文,您将了解到: Method Swizzling(动态方法交换)简介 Method Swizzlin ...
- iOS总结-Runtime篇之黑魔法Method Swizzling的滥用会有危险吗
参考https://www.jianshu.com/p/19c5736c5d9a, http://blog.sina.com.cn/s/blog_a343f32b0101en4o.html runti ...
- Method Swizzling的各种姿势
因为Objective-C的runtime机制, Method Swizzling这个黑魔法解决了我们实际开发中诸多常规手段所无法解决的问题, 比如代码的插桩,Hook,Patch等等. 我们首先看看 ...
- 本文将要讨论Objective-C中的方法替换(method replacement)和swizzling(移魂大法)。
重写类的方法(Overriding Methods) Overriding methods在任何面向对象语言中都很常见,主要用于子类化中.在子类中复写一个方法,然后在子类的实例就可以使用这个被重写的方 ...
- runtime实践之Method Swizzling
利用 Objective-C 的 Runtime 特性,我们可以给语言做扩展,帮助解决项目开发中的一些设计和技术问题.这一篇,我们来探索一些利用 Objective-C Runtime 的黑色技巧.这 ...
- Objective-C Runtime 运行时之四:Method Swizzling
理解Method Swizzling是学习runtime机制的一个很好的机会.在此不多做整理,仅翻译由Mattt Thompson发表于nshipster的Method Swizzling一文. Me ...
- iOS总结-Runtime篇之用途及面试题的总结一黑魔法Method Swizzling
runtime的用途基本分下面几类: 实现多继承Multiple Inheritance 上一篇里面的最后利用methodSignatureForSelector来进行一次转发,在forwardInv ...
最新文章
- torch 和torchvision对应关系并附下载路径
- 人与动物之间有没有一条不可逾越的鸿沟?——从基础存在论到生命哲学
- 洛谷P2178 品酒大会
- HDLBits 系列(18) BCD码计数器的设计
- GRE作文用AI打分,已经20周年了:AI给中国考生的分数,远高于人类打分
- 计算机5800怎么开机,神舟5800笔记本怎么进bios
- mysql explain的使用(优化查询)
- python中的拷贝
- java当中有关循环的代码_有关Java循环的内容,编程中还是比较常用的,下面分享给大家几个循环的示例代码,练习一下。1、循环输出1到100之间所有能被3或能被4整除的数。pack...
- matlab调用sh函数,Shell 函数定义与调用
- ASP.NET关于WebPages的一点总结
- 一份超详细的MySQL高性能优化实战总结!
- webservice studio 参数是DataSet时不支持中文 解决方法
- 第 14 章 垃圾回收概述
- JavaScript函数—JavaScript闭包
- 网易云音乐云盘上传歌词的方法
- java程序的编程规范
- 一米村长讲故事机器人_又一位主持人跑去给孩子讲故事,村长李锐推出有声故事品牌“村长讲故事”...
- android 加固崩溃,360加固后,夜神模拟器安装崩溃
- Oracle sql 按指定顺序产生序列号
热门文章
- 32. 脱壳篇-简单带壳的程序、反调试带壳的程序(堆栈平衡原理找OEP、代码段设置断点)
- MySql 5.6.36 64位绿色版安装
- 深入了解Debug和Release的区别
- json数组传递到后台controller
- AJAX异步检查,检查用户名是否存在
- java.net.ConnectException: Connection refused: connect
- Android Eclipse ADT使用Tips
- SharePoint 2013中修改windows 活动目录(AD)域用户密码的WebPart(免费下载)
- DP-访问者模式(Visitor Pattern)
- sourceforge.net cvs sample