初识 Runtime

前言

之前在看一些第三方源码的时候,时不时的能碰到一些关于运行时相关的代码。于是乎,就阅读了一些关于运行时的文章,感觉写的都不错,写此篇文章为了记录一下,同时也重新学习一遍。

Runtime简介

  • Runtime简称运行时,OC就是运行时机制。
  • C语言中函数的调用在编译的时候就会决定调用哪个函数。
  • 对于OC来说,属于动态调用过程,在编译的时候并不能决定调用哪个函数,只有真正运行的时候才会根据函数的名称找到对应的函数来调用。
  • 事实证明:
    1. 在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明就不会报错。
    2. 在编译阶段,C语言调用未实现的函数就会报错。

Runtime的作用

发送消息

  • 方法调用的本质就是向对象发送消息。
  • objc_msgSend,只有对象才能发送消息,因此以objc开头。注意:在oc中,不论是实例对象还是Class,都是id类型的对象
  • 让我们来看看方法调用转化成运行时的代码,看看调用方法的真面目吧。

总结:到这里,我们可以看到调用方法的本质就是发送消息了,并且可以看到我们写的 NSObject *obj = [[NSObject alloc] init]; 上面这条语句发送了两次消息,第一次发送了alloc消息,第二次发送了init消息。

给分类添加属性

相信大家都知道分类是不可以添加属性的,不过我们可以通过运行时,给分类动态的添加属性。

原理:给一个分类声明属性,其本质就是给这个类添加关联,并不是直接把这个值的内存空间添加到类存空间。

实践

  • 我们给NSObject添加一个分类,然后声明一个name属性。
#import <Foundation/Foundation.h>     
@interface NSObject (Extension)     
@property (nonatomic, copy) NSString *name;     
@end
  • 我们创建一个NSObject对象,然后给name属性赋值,并且打印name的值。 我们可以看到编译成功,但是运行的时候就会华丽丽的崩溃,这就是所谓的不能给分类添加属性的原因了。不过我们可以通过运行时实现。
  • 接下来我们在.m文件重写name属性的setter以及getter方法。
  • 运行程序,编译成功,运行也成功。

动态添加方法

  • 开发使用场景: 如果一个类方法非常多,加载该类到内存的时候比较耗费资源,需要给每个方法生成映射表,可以使用运行时给该类动态添加方法来解决。(ps:我感觉这个在开发中不常用)

实践

  • 创建一个继承NSObjectStudent类,声明一个study方法。
#import <Foundation/Foundation.h>     
@interface Student : NSObject     
-(void)study;     
@end
  • 创建一个Student对象,调用study方法。
Student *s = [[Student alloc] init];    
[s performSelector:@selector(study)];

此时会出现一个经典的报错: -[Student study]: unrecognized selector sent to instance 0x7fd719cbb2f0 错误原因:调用一个未实现的实例方法

  • 我们在Student类.m文件动态添加study方法的实现。

交换方法实现

  • 交换方法实现,也就是所谓的Method Swizzling
  • 场景一:如果你整个项目都做完了,然后产品经理告诉你想统计每一个页面停留的时长。
  • 场景二:系统自带的方法功能不够,给系统自带的方法扩展一些功能,并且保持原有功能。

实践

场景一:统计整个项目每一个页面停留时长。(解决方法也不是唯一的)

可是一开始写项目的时候,并没有使用到继承,所以又papapa地就整个项目的控制器都继承于一个基类,重复地将每一个控制器的继承都该成了我们创建的基类。但是,这样解决真的好么,有可能我们有些界面是继承自UITableViewController的,UICollectionViewController,等等。那么你就可能会对这些控制器再单独的写上面的代码了。 好不容易将整个项目改过来了,然后某天,公司来了一位新人,你告诉他所有的类都要继承自你写的那个基类,新手总是会不经意地犯错误(也有可能是人家还没有习惯),有些类忘记继承了,后期排查起来费力费时。那么有没有更好地解决方式呢?方式三就可以处理这种问题。

  • 方式三:使用Method Swizzling实现,给UIViewController写一个分类。

字典转模型

这个单独开一篇给大家讲讲吧。

结束语

希望通过本文能让大家学习到一些关于Runtime的知识,如果有什么疑问,欢迎大家一起讨论。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏李蔚蓬的专栏

小结:greenDAO和LitePal的区别

1. greenDAO的version等数据库属性设置都是在对应的模型类里面完成的,以Java class的属性变量的形式存储;而LitePal是在另外的一个x...

2981
来自专栏desperate633

深入理解Java多线程(multiThread)多线程的基本概念线程同步wait,notify,notifyAll线程的生命周期

一个java程序启动后,默认只有一个主线程(Main Thread)。如果我们要使用主线程同时执行某一件事,那么该怎么操作呢? 例如,在一个窗口中,同时画两排...

1342
来自专栏极客编程

linux下的shell脚本编程

Shell本身是一个用C语言编写的程序,它是用户使用Unix/Linux的桥梁,用户的大部分工作都是通过Shell完成的。Shell既是一种命令语言,又是一种程...

1312
来自专栏IMWeb前端团队

Node中没搞明白require和import,你会被坑的很惨

ES6标准发布后,module成为标准,标准的使用是以export指令导出接口,以import引入模块,但是在我们一贯的node模块中,我们采用的是Common...

3048
来自专栏张善友的专栏

Redis应用场景

Redis开创了一种新的数据存储思路,使用Redis,我们不用在面对功能单调的数据库时,把精力放在如何把大象放进冰箱这样的问题上,而是利用Redis灵活多变的数...

2396
来自专栏javathings

volatile 解决了什么问题?

volatile 关键字是最常问到的问题,关于这个关键字的作用解释,网上的文章已经多如牛毛了。

2523
来自专栏猿人谷

用C来实现内存池

介绍:        设计内存池的目标是为了保证服务器长时间高效的运行,通过对申请空间小而申请频繁的对象进行有效管理,减少内存碎片的产生,合理分配管理用户内存,...

4817
来自专栏大前端_Web

从前端模块化的概念来理解Webpack

版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 https://blog.csdn.net/wkyseo/articl...

1553
来自专栏移动开发

Android 代码规范(自用)

以色值本身的名字来命名,而不是以功能性的名称表达如 btn_bg, tv_bg 这样很容易有些色值重复.

1542
来自专栏散尽浮华

linux下截取给定路径中的目录部分

在日常运维中,有时会要求截取一个路径中的目录部分。 截取目录的方法,有以下两种: 1)dirname命令(最常用的方法):用于取给定路径的目录部分。很少直接在s...

2285

扫码关注云+社区

领取腾讯云代金券