1.给分类动态添加属性

FDFullscreenPopGesture中给UIViewController的分类里有这么一个属性:

@property (nonatomic, copy) _FDViewControllerWillAppearInjectBlock fd_willAppearInjectBlock;

这是一个block的属性,block定义如下:

typedef void (^_FDViewControllerWillAppearInjectBlock)(UIViewController *viewController, BOOL animated);

看到这里也许你会提问,OC中不是不能给分类添加属性么?正常情况下,OC是不允许给OC添加属性的。但是利用Runtime的特性,这是可以办到的。实现方法如下:

- (_FDViewControllerWillAppearInjectBlock)fd_willAppearInjectBlock
{
return objc_getAssociatedObject(self, _cmd);// 根据关联的key,获取关联的值。这里的key等于_cmd,_cmd等于fd_willAppearInjectBlock
} - (void)setFd_willAppearInjectBlock:(_FDViewControllerWillAppearInjectBlock)block
{
// 第一个参数:给哪个对象添加关联
// 第二个参数:关联的key,通过这个key获取
// 第三个参数:关联的value
// 第四个参数:关联的策略
objc_setAssociatedObject(self, @selector(fd_willAppearInjectBlock), block, OBJC_ASSOCIATION_COPY_NONATOMIC);//关联对象
}

动态给分类添加属性的方法是:

objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

获取这个属性的方法是:

objc_getAssociatedObject(id object, const void *key)

还有一个方法是移除属性:

objc_removeAssociatedObjects(id object)

是的,这样就动态的给UIViewController的分类添加了fd_willAppearInjectBlock这么一个属性。

NOTE:在使用Runtime的这些方法的时候不要忘了导入objc/runtime.h这个头文件哦!

2.动态添加方法

要想动态添加方法我们必须了解方法是如何执行的,通常我们调用方法是通过[object message]这种方法,除了这种方法还有一种是比较少用的,就是[object performSelector:@selector(message)]这种方式。通过下面这张图我们可以了解一下他们对消息的处理的不同之处。

iOS消息转发.png

通过上图,我们可以得知,要想动态添加方法必须是通过[object performSelector:@selector(message)]这种方式调用方法才能在运行时阶段通过Runtime的一些方法达到动态的添加方法。如果现在有一个Person类,在其它地方通过performSelector的方式调用Personrun方法。但是Person类中并没有实现这个方法。

Person p = [Person alloc] init];
// 这个时候即使Person类没有实现run方法编译器也不会报错
[p performSelector:@selector(run)];

这时候只需要在Person中实现resolveInstanceMethod:方法就可以达到动态添加方法的目的。

//首先我们要在Person类里面实现我们要动态添加的方法
// 要注意,默认方法都有两个隐式参数
void run(id self,SEL sel){
NSLog(@"%@ %@",self,NSStringFromSelector(sel));
}
// 当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来.
// 刚好可以用来判断未实现的方法是不是我们想要动态添加的方法
+ (BOOL)resolveInstanceMethod:(SEL)sel{
//先判断一下传过来的是不是run方法
if (sel == @selector(run)){
//如果是run方法就动态添加run方法
class_addMethod(self.class, @selector(run),(IMP)run, "v@:");
// 第一个参数:给哪个类添加方法
// 第二个参数:添加方法的方法编号
// 第三个参数:添加方法的函数实现(函数地址),如果是OC方法
//可以用+(IMP)instanceMethodForSelector:(SEL)aSelector;获得方法的实现。
// 第四个参数:方法的签名,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
}
}

这样就达到了给一个类动态添加方法的效果了,如果想把方法转发给其他的类实现,需要处理消息转发的第二或第三个函数了。

3.替换系统自带的方法

当一些时候,系统自带效果满足不了我们的时候,要么我们自定义,要么直接替换系统的方法。在公有的API是没有方法办到的。我们来看一段FDFullscreenPopGesture的代码(注释是我加的):

+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class]; //获取系统方法的SEL
SEL originalSelector = @selector(viewWillAppear:);
//获取替换方法的SEL
SEL swizzledSelector = @selector(fd_viewWillAppear:);
//为了获取IMP指针,获得方法的Method
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
//为了安全起见,先判断是否已经存在要交换的方法
BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (success) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
} - (void)fd_viewWillAppear:(BOOL)animated
{
//不要认为这句代码有错,其实很好理解,在调用这句的时候方法已经交换了
// Forward to primary implementation.
[self fd_viewWillAppear:animated]; if (self.fd_willAppearInjectBlock) {
self.fd_willAppearInjectBlock(self, animated);
}
}

通过上面的代码我们可以看出来,替换系统自带的方式实现需要用到的重要方法是method_exchangeImplementations()方法,并且要注意替换方法里面对自己的调用。这个方法也就是人们常说的Method Swizzling黑魔法,用的时候要注意,这是一把双刃剑!

结尾

Runtime在项目中很少用,但是要理解它,理解了之后用起来也不危险。如果你喜欢我的文章,不妨扫一扫下面的二维码请我喝杯茶。祝大家在iOS开发的道路上玩得愉快!

Runtime常用的几个场景的更多相关文章

  1. Runtime常用的几个应用场景

    Runtime常见的几个应用场景. Runtime常见应用场景 具体应用拦截系统自带的方法调用(Method Swizzling黑魔法) 实现给分类增加属性 实现字典的模型和自动转换 JSPatch替 ...

  2. iOS开发之Runtime常用示例总结

    经常有小伙伴私下在Q上问一些关于Runtime的东西,问我有没有Runtime的相关博客,之前还真没正儿八经的总结过.之前只是在解析第三方框架源码时,聊过一些用法,也就是这些第三方框架中用到的Runt ...

  3. Java 常用List集合使用场景分析

    Java 常用List集合使用场景分析 过年前的最后一篇,本章通过介绍ArrayList,LinkedList,Vector,CopyOnWriteArrayList 底层实现原理和四个集合的区别.让 ...

  4. redis五种数据类型和常用命令及适用场景

    一.redis的5种数据类型: 1.基础理解: string 字符串(可以为整形.浮点型和字符串,统称为元素) list 列表(实现队列,元素不唯一,先入先出原则) set 集合(各不相同的元素) h ...

  5. jedis实现操纵redis的常用api及使用场景

    简单记录一下,和描述一下常用的业务场景.好记性不如烂笔头. pom.xml <!--整合redis--> <dependency> <groupId>redis.c ...

  6. php数组去重、魔术方法、redis常用数据结构及应用场景

    一.用函数对数组进行去重的方法 1.arrau_unique函数的作用 移除数组中重复的值. 将值作为字符串进行排序,然后保留每个值第一次出现的健名,健名保留不变. 第二个参数可以选择排序方式: SO ...

  7. Redis常用数据类型及使用场景

    Redis最为常用的数据类型 字符串(String) 字符串列表(list) 字符串集合(set) 哈希(hash) 有序的字符串集合(sorted set) String(字符串) 字符串是最基本的 ...

  8. Redis数据类型的常用API以及使用场景

    一.通用命令 1.keys  遍历出所有的key 一般不在生产环境使用 2.dbsize key的总数 3.exists key 4.del key  删除指定key-value 5.expire k ...

  9. Unity学习笔记(3):一些常用API和应用场景

    Mathf.Lerp(float a,float b,float t)插值函数,当a < b时往a中插入t,以此来实现颜色,声音等渐变效果. GameObject.FindWithTag(str ...

随机推荐

  1. sql server 新语法 收藏

    1.行转列 PIVOT函数,行转列,列转换UNPIVOT select * from ShoppingCart as C PIVOT(count(TotalPrice) FOR [Week] IN([ ...

  2. 利用selenium爬取京东商品信息存放到mongodb

    利用selenium爬取京东商城的商品信息思路: 1.首先进入京东的搜索页面,分析搜索页面信息可以得到路由结构 2.根据页面信息可以看到京东在搜索页面使用了懒加载,所以为了解决这个问题,使用递归.等待 ...

  3. 用python发送短消息(基于阿里云平台)

    新版短信接口在线测试页面:https://api.aliyun.com/new#/?product=Dysmsapi&api=SendSms&params={}&tab=DEM ...

  4. Ontology理论研究和应用建模

    转自:https://www.cnblogs.com/yes-V-can/p/8151275.html 目录 1 关于Ontology 1.1 Ontology的定义 1.2 Ontology的建模元 ...

  5. 一个萌新对redis的理解

    redis是用来保存一些常用的数据到内存,以加快数据读取,减少直接访问DB流量以降低DB压力.既然是放到内存的,那我们怎么样保证用户使用的时候不会出现与数据的差异呢,其实这叫“如何报证缓存数据的一致性 ...

  6. python内置函数 和模块函数总结

    1.内置函数(无需导入)long() 函数将数字或字符串转换为一个长整型.len() 统计元素个数print() 打印,输出input() 输入,或阻塞程序运行type 获取类型range 产生连续的 ...

  7. python学习Day10 函数的介绍(定义、组成、使用)

    今日学习内容: 1.什么是函数 :函数就是一个含有特定功能的变量,一个解决某问题的工具 函数的定义:通过关键字def + 功能名字():代码体(根据需求撰写代码逻辑) 2.为什么要用函数:可以复用:函 ...

  8. 学生管理系统(javaweb版)

    准备用javaweb 的技术做一个简单的学生管理系统 打算不用登陆的那种,直接进入管理界面. 完成增删改查的功能. 慢慢开始更新,先写那么多.

  9. 如何把ppt写好

    前言 这里不是总结ppt如何写的美观漂亮,而是总结写整体的结构,如何修辞 整体结构 前后呼应 就像做作文一样,前后呼应,产生共鸣和联想,切记过于离散 抽象化 不要把自己做的东西写小了,要有一定的抽象度 ...

  10. python字符串之format格式化函数

    学习中~ 觉得应该系统地学习一下python,今天学习了字符串,以下是自己的笔记. 首先说一下format函数,用{}和:代替了%,比如: >>>“{} {} {}”.format( ...