1.RunTime消息机制

1.消息机制是运行时里面最重要的机制,OC中任何方法的调用,本质都是发送消息。

当我们实例化这个对象时:

MyClass *object = [[MyClass alloc] init]; 就会调这个实例化方法:[object showUserName];

我们大概来看一下它的底层实现:

•在编译阶段,[object showUserName] 会被编译器转化为: objc_msgSend(object, "showUserName"),相当于一种“发消息”的行为。

• 在运行阶段,执行到上述的objc_msgSend这个函数时,函数内部会到object对应的内存地址,寻找showUserName这个方法的地址并执行。如果找不到,就会抛一个“unknown selector sent to instance”的异常。

证明过程:
在终端用命令打开此类文件所在的文件夹,继续写入命令:

clang -rewrite-objc MyClass.m(把oc代码转写成

c/c++代码,我们常用它来窥探OC的底层实现),不一会在原来的同一目录下会多出一个 MyClass.cpp 文件

双击打开,可以看到

init 方法已经被编译器转化为下面这样:

objc_msgSend 函数被定义在 objc/message.h 目录下,其函数原型是:

OBJC_EXPORT void objc_msgSend(void /* id self, SEL op, ... */ )

该函数有两个参数,一个

id 类型(消息接收对象),一个 SEL 类型(方法的selector)。

@selector (SEL):是一个SEL方法选择器。SEL其主要作用是快速的通过方法名字查找到对应方法的函数指针,然后调用其函数。SEL其本身是一个Int类型的地址,地址中存放着方法的名字。

对于一个类中,每一个方法对应着一个

SEL。所以一个类中不能存在2个名称相同的方法(有歧义。。。),即使参数类型不同,因为SEL是根据方法名字生成的,相同的方法名称只能对应一个SEL。

歧义解释:

- (void)go {} + (void)go {} 这两个方法可以共存(我们知道,这两个方法的名字都是go)。

我个人的理解是:当我们向一个对象或一个类发送消息时,

runtime都会根据方法名去这个对象所属的这个类的方法列表中查找方法,而方法列表的外层应该是一个字典,根据所传的接收消息对象不同,查找的方法列表也不同。

objc_msgSend([MyClass class], @selector(go));

objc_msgSend([[MyClass alloc] init], @selector(go));

Id :是一个结构体指针类型,它可以指向 Objective-C 中的任何对象。

Class vclass = NSClassFromString(@"ViewController");

id vc = [[vclass alloc] init];

objc_object 结构体定义如下:

struct objc_object { Class isa OBJC_ISA_AVAILABILITY;};

这就是我们通常所说的对象,这个结构体只有一个成员变量

isa,对象可以通过 isa 指针找到其所属的类。isa 是一个 Class 类型的成员变量,那么 Class 又是什么呢?如下:

•·Class 也有一个 isa 指针,指向其所属的元类(meta)。

•·super_class:指向其超类。

•·name:是类名。

•·version:是类的版本信息。

•·info:是类的详情。

•·instance_size:是该类的实例对象的大小。

•·ivars:指向该类的成员变量列表。

•·methodLists:指向该类的实例方法列表,它将方法选择器和方法实现地址联系起来。methodLists 是指向 ·objc_method_list 指针的指针,也就是说可以动态修改 *methodLists 的值来添加成员方法,这也是 Category 实现的原理,同样解释了 Category 不能添加属性的原因。

•·cache:Runtime 系统会把被调用的方法存到 cache 中(理论上讲一个方法如果被调用,那么它有可能今后还会被调用),下次查找的时候效率更高。

•·protocols:指向该类的协议列表。

经过以上的讲述,我们大概可以了解到,当调用一个方法时,其运行过程大致如下:

•底层调用 [p performSelector:@selector(eat)] 方法,编译器在将代码转化为 objc_msgSend(p, @selector(eat))把方法的调用者和方法选择器当做参数传递过去。

•首先,检测这个 selector 的 target 是不是 nil,Objc 允许我们对一个 nil 对象执行任何方法不会 Crash,因为运行时会被忽略掉。

•如果target != nil,方法的调用者会通过 isa 指针来找到其所属的类,然后在 cache 中查找该方法,找得到就跳到对应的方法去执行。

•若 cache 中未找到,再去 methodList 中查找。若能找到,则将 method 加入到 cache 中,以方便下次查找,并通过 method 中的函数指针跳转到对应的函数中去执行。

•若 methodlist 中未找到,则去 superClass 中查找,一直找到 NSObject 类为止。若能找到,则将 method 加入到 cache 中。

•如果还是查不到,则会执行消息转发,抛出异常。

我们来看看底层调用方法的几个实例(分别是无参无返回,有参无返回,有参有返回)

2.RunTime交换方法

应用场景:当系统自带的方法功能不够,需要给系统自带的方法扩展一些功能时。

eg:实现image添加图片的时候,自动判断image是否为空,如果为空则提醒图片不存在。

有以下三种比较好的解决方法

:

1.自定义类, 重写系统自带的imageName:方法,这种方法虽然可以实现,但是它的弊端就是必须要使用自己的类,依赖性强。

2.给UIImage添加一个分类, 改变系统类的实现,给系统的类添加方法的时候调用(每次使用都需要导入头文件,并且如果项目比较大,之前使用的方法全部需要更改)。

3.使用runtime的交互方法,给系统的方法添加功能. 具体实现 : 添加一个分类 --> 在分类中提供一个自定义判空增加新功能的方法 --> 将这个方法的实现和系统自带的方法的实现交互.

交换方法的本质其实是交换两个方法的实现,即调换

le_imageNamed:和imageName:方法,达到调用le_imageNamed:其实就是调用imageNamed:方法的目的。

那么首先需要明白方法在哪里交换,因为以后都是使用自己定义的方法取代系统的方法,所以,当程序一启动,就要求能使用自己定义的功能方法。我们一般在

+ (void)load 方法里实现交换方法 (当程序启动的时候就会调用该方法,换句话说,只要程序一启动就会调用load方法,整个程序运行中只会调用一次)

•注意:交换方法时 le_imageNamed:方法中就不能再调用imageNamed:方法了,因为调用imageNamed:方法实质上相当于调用 le_imageNamed:方法,会循环引用造成死循环。

想要学习前端开发的同学,可以加群:

543627393 学习哦!

runtime—新手必学相关推荐

  1. python基础知识整理-整理了27个新手必学的Python基础知识点

    原标题:整理了27个新手必学的Python基础知识点 1.执行脚本的两种方式 Python a.py 直接调用Python解释器执行文件 chomd +x a.py ./a.py #修改a.py文件的 ...

  2. python3.8.5怎么用-Python 3.8 新功能大揭秘【新手必学】

    最新版本的Python发布了!今年夏天,Python 3.8发布beta版本,在2019年10月14日,第一个正式版本已准备就绪.现在,我们都可以开始使用新功能并从最新改进中受益. Python 3. ...

  3. python3.8怎么打开创建_Python 3.8 新功能大揭秘【新手必学】

    最新版本的Python发布了!今年夏天,Python 3.8发布beta版本,在2019年10月14日,第一个正式版本已准备就绪.现在,我们都可以开始使用新功能并从最新改进中受益. Python 3. ...

  4. python桌面翻译_Python实现桌面翻译工具【新手必学】

    Python 用了好长一段时间了,起初是基于对爬虫的兴趣而接触到的.随着不断的深入,慢慢的转了其它语言,毕竟工作机会真的太少了.很多技能长时间不去用,就会出现遗忘,也就有了整理一下,供初学者学习和讨论 ...

  5. 新手必学的几个视频剪辑技巧

    在视频崛起的时代,B站和抖音等平台的视频都非常火爆,如果你还不会剪辑视频怎么办?今天小编给大家分享一些技巧,轻松上手,学会剪辑. 首先要给大家推的是一个新手和老手都必选的剪辑工具--视频剪辑高手,界面 ...

  6. 【前端工程化】配置package.json中scripts命令脚本,新手必学

    每日鸡汤:你总要努力追上那个曾经被赋予众望的自己吧 目录 前言 一.运行npm run 命令之后会干啥? 1. scripts里面写啥 2. node_modules/.bin 二进制可执行文件 二. ...

  7. python爬虫模块取cookie_Python爬虫之cookie的获取、保存和使用【新手必学】

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:huhanghao Cookie,指某些网站为了辨别用户身份.进行s ...

  8. python编程规范 谷歌_Python最新编程规范,新手必学

    最近,团队又来了几个小伙伴,经过一段时间磨合之后,发现彼此之间还是比较默契的,但有一个很大的问题是,每个人的编程风格和习惯都不同,导致现在代码看起来非常混乱. 这里还要注意:不管你是想学Python还 ...

  9. python 自动输入_Python自动输入【新手必学】

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:哈喽哈嘿哈 这篇文章是我的第一篇文章,写的不好的地方,请大家多多指教哈,另 ...

最新文章

  1. Facebook增强版LASER开源:零样本迁移学习,支持93种语言
  2. 从CPU缓存看缓存的套路
  3. Java:获取数组中的子数组的多种方法
  4. mysql forget the password
  5. C语言经典例19-完数
  6. 51nod 1770 数数字 找规律,注意进位,时间复杂度O(n)
  7. 终极会话劫持工具SSClone
  8. 如何使Mac Docker支持SQL on Linux容器Volume特性
  9. Django 遇到的错误:expected str, bytes or os.PathLike object, not _io.TextIOWrapper
  10. java 封装(Encapsulation)
  11. QCC3005 芯片开发过程中碰到的一些问题
  12. Q3面试嵌入式软件工程师的面试经验
  13. 容斥原理(转载http://www.cppblog.com/vici/archive/2011/09/05/155103.html)
  14. 一枚菜鸟前端工程师月度工作总结
  15. Ubuntu安装GIMP
  16. Android NDK-EGL 初级
  17. 前程似锦用计算机怎么表示,2021选什么专业 毕业后前程似锦
  18. 小米手机比较 联通、移动、电信 3G 支持比较
  19. 高德尝试用“成本价”推动共享,但高精地图行业不只有价格
  20. SPDY:一种更快速web的实验协议(转)

热门文章

  1. QTP中VBS脚本下FSO、WSH的应用(二)
  2. 6间房,把评论添加到视频的metadata.
  3. IE8中如何添加Activity
  4. 一天完成一点,进度太慢了啊
  5. 注解的原理又是怎么一回事
  6. 关于JFace中的向导式对话框(WizardDialog类)
  7. IOS之NSValue整理
  8. 一起谈.NET技术,HubbleDotNet 和 Lucene.Net 匹配相关度的比较
  9. DiskGenius的 “终止位置参数溢出”错误解决方法。
  10. Delphi 与 DirectX 之 DelphiX(80): TDIB.BlendPixel();