需求

就拿我们公司项目来说吧,我们公司是做导航的,而且项目规模比较大,各个控制器功能都已经实现。突然有一天老大过来,说我们要在所有页面添加统计功能,也就是用户进入这个页面就统计一次。我们会想到下面的一些方法:

手动添加

直接简单粗暴的在每个控制器中加入统计,复制、粘贴、复制、粘贴...
上面这种方法太Low了,消耗时间而且以后非常难以维护,会让后面的开发人员骂死的。

继承

我们可以使用OOP的特性之一,继承的方式来解决这个问题。创建一个基类,在这个基类中添加统计方法,其他类都继承自这个基类。

然而,这种方式修改还是很大,而且定制性很差。以后有新人加入之后,都要嘱咐其继承自这个基类,所以这种方式并不可取。

Category

我们可以为UIViewController建一个Category,然后在所有控制器中引入这个Category。当然我们也可以添加一个PCH文件,然后将这个Category添加到PCH文件中。

我们创建一个Category来覆盖系统方法,系统会优先调用Category中的代码,然后在调用原类中的代码。

我们可以通过下面的这段伪代码来看一下:

#import "UIViewController+EventGather.h"
@implementation UIViewController (EventGather)
- (void)viewDidLoad {NSLog(@"页面统计:%@", self);
}
@end
Method Swizzling

我们可以使用苹果的“黑魔法”Method SwizzlingMethod Swizzling本质上就是对IMPSEL进行交换。

Method Swizzling原理

Method Swizzing是发生在运行时的,主要用于在运行时将两个Method进行交换,我们可以将Method Swizzling代码写到任何地方,但是只有在这段Method Swilzzling代码执行完毕之后互换才起作用。

而且Method Swizzling也是iOSAOP(面相切面编程)的一种实现方式,我们可以利用苹果这一特性来实现AOP编程。

首先,让我们通过两张图片来了解一下Method Swizzling的实现原理

图一

图二

上面图一中selector2原本对应着IMP2,但是为了更方便的实现特定业务需求,我们在图二中添加了selector3IMP3,并且让selector2指向了IMP3,而selector3则指向了IMP2,这样就实现了“方法互换”。

OC语言的runtime特性中,调用一个对象的方法就是给这个对象发送消息。是通过查找接收消息对象的方法列表,从方法列表中查找对应的SEL,这个SEL对应着一个IMP(一个IMP可以对应多个SEL),通过这个IMP找到对应的方法调用。

在每个类中都有一个Dispatch Table,这个Dispatch Table本质是将类中的SELIMP(可以理解为函数指针)进行对应。而我们的Method Swizzling就是对这个table进行了操作,让SEL对应另一个IMP


Method Swizzling使用

在实现Method Swizzling时,核心代码主要就是一个runtime的C语言API:
OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
实现思路

就拿上面我们说的页面统计的需求来说吧,这个需求在很多公司都很常见,我们下面的Demo就通过Method Swizzling简单的实现这个需求。

我们先给UIViewController添加一个Category,然后在Category中的+(void)load方法中添加Method Swizzling方法,我们用来替换的方法也写在这个Category中。由于load类方法是程序运行时这个类被加载到内存中就调用的一个方法,执行比较早,并且不需要我们手动调用。而且这个方法具有唯一性,也就是只会被调用一次,不用担心资源抢夺的问题。

定义Method Swizzling中我们自定义的方法时,需要注意尽量加前缀,以防止和其他地方命名冲突,Method Swizzling的替换方法命名一定要是唯一的,至少在被替换的类中必须是唯一的。

#import "UIViewController+swizzling.h"
#import <objc/runtime.h>
@implementation UIViewController (swizzling)+ (void)load {// 通过class_getInstanceMethod()函数从当前对象中的method list获取method结构体,如果是类方法就使用class_getClassMethod()函数获取。Method fromMethod = class_getInstanceMethod([self class], @selector(viewDidLoad));Method toMethod = class_getInstanceMethod([self class], @selector(swizzlingViewDidLoad));/***  我们在这里使用class_addMethod()函数对Method Swizzling做了一层验证,如果self没有实现被交换的方法,会导致失败。*  而且self没有交换的方法实现,但是父类有这个方法,这样就会调用父类的方法,结果就不是我们想要的结果了。*  所以我们在这里通过class_addMethod()的验证,如果self实现了这个方法,class_addMethod()函数将会返回NO,我们就可以对其进行交换了。*/if (!class_addMethod([self class], @selector(swizzlingViewDidLoad), method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {method_exchangeImplementations(fromMethod, toMethod);}
}// 我们自己实现的方法,也就是和self的viewDidLoad方法进行交换的方法。
- (void)swizzlingViewDidLoad {NSString *str = [NSString stringWithFormat:@"%@", self.class];// 我们在这里加一个判断,将系统的UIViewController的对象剔除掉if(![str containsString:@"UI"]){NSLog(@"统计打点 : %@", self.class);}[self swizzlingViewDidLoad];
}
@end

看到上面的代码,肯定有人会问:楼主,你太粗心了,你在swizzlingViewDidLoad方法中又调用了[self swizzlingViewDidLoad];,这难道不会产生递归调用吗?
答:然而....并不会

iOS黑魔法-Method Swizzling相关推荐

  1. iOS黑魔法--Method Swizzling

    参考链接: iOS黑魔法-Method Swizzling iOS 黑魔法 - 专题 - 简书 Method Swizzle黑魔法,修改 ios 系统类库方法

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

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

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

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

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

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

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

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

  6. 关于Swift4.0 Method Swizzling(iOS的hook机制)使用

    2019独角兽企业重金招聘Python工程师标准>>> 关于Method Swizzling 原理什么的有很多帖子讲述的已经很清楚这里不再赘述, 这里仅仅处理Method Swizz ...

  7. ios 黑魔法 解决问题

    关于黑魔法解决问题,有很多文章,下面推荐一篇比较好的,也算自己收藏的. iOS黑魔法-Method Swizzling http://www.tuicool.com/articles/jmYVni

  8. Method Swizzling的各种姿势

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

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

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

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

    Objective-C Runtime (三):Method Swizzling(方法替换) Method Swizzling是一种改变改变一个'selector'的实际实现的技术.通过这一技术,我们 ...

最新文章

  1. SpringBoot 集成 WebSocket,实现后台向前端推送信息
  2. Html学习总结(2)——Html页面head标签元素的意义和应用场景
  3. CCF NOI1065 最小公倍数
  4. ubuntu下C语言编程的注意点
  5. 嵌入式linux寄存器移位寻址,嵌入式系统及应用》 (32+16)教学大纲
  6. win下MySQL 8.0.11 修改密码、开启远程访问
  7. Oracle 9i10g编程艺术 深入数据库体系结构
  8. Skyline软件二次开发初级——11如何在WEB页面中的三维地图上加载和保存工程文件...
  9. 无人驾驶全局路径规划之A星算法
  10. android9 apk自动安装功能,Android自动安装APK
  11. 怎么用Python爬取抖音小视频? 资深程序员都这样爬取的(附源码)
  12. 解决IndexError: Target 2 is out of bounds.
  13. linux 桌面 修复工具下载,恢复ubuntu20.04默认桌面管理器
  14. Qwt使用之QwtPlot
  15. 微信会员卡开发之微信公众平台的基本配置
  16. 表格无法无法计算机,无法打开Excel表的几种原因的解决方案
  17. Java的JVM运行时栈结构和方法调用详解
  18. c语言开发ETL,【ETL开发工作内容|工作职责|ETL开发做什么】-看准网
  19. Aresio Web 2.0网页UI图标素材分享下载[微盘地址]
  20. 【Salesforce / LWC】LWC中使用第三方库开发 Use Third-Party JavaScript Libraries In LWC

热门文章

  1. 如何让用户留在生态系统里?向苹果学习!【转载】
  2. 值得学习的100个网站推广方法。新站推广必备
  3. Centos7配置ThinkPHP5.0完整过程(二)
  4. 数据结构——二叉查找树
  5. Vue2 后台管理系统解决方案
  6. 使用 Eclipse 插件部署 Java 应用
  7. HTML5之2D物理引擎 Box2D for javascript Games 系列 第二部分
  8. Reaction to 构造之法 of Software Engineering From The First Chapter toThe Fifth Chapter
  9. DOM 精简知识教程
  10. 使用Xcode Instruments Leak解决内存泄漏问题