Objective-C Runtime 消息机制

参考自
http://mp.weixin.qq.com/s?__biz=MzA3ODkzOTQ2NA==&mid=2651911361&idx=1&sn=592998bbdfd7c747b412382e4b472c8b&chksm=845f40e6b328c9f0aba24855444902c62d5ec9cd8a6278d1e8e2fd75bd0cf7d6843b6e7e4fb1&scene=0#rd”这里

一:Objective-C Runtime 是什么
什么是Objective-C Runtime,在这里举个例子,同是也证明一下Objective-C Runtime与我们平常开发息息相关;比如给button添加点击事件的时候的Selector:
[button ad]
为什么acton参数传递的是一个Selector,而不是一个函数名呢?它与函数和方法有什么不同?
这里写图片描述
从面向对象角度,为调用Dog对象的eat方法;从Objective-C Runtime是发送消息

二:objc_msgSend
上面对象调用方法编译器会将它转化为一个函数调用
这里写图片描述
objc_msgSend是Objective-C Runtime中的函数,定义在头文件这里写图片描述
总结:其实所有通过一对方括号进行调用的方法,都是通过Objective-C Runtime的objc_msgSend函数发送的一个消息传递。
那objc_msgSend这个函数到底做了什么?
这里写图片描述
在这之前我们应该了解一下Runtime中关于类的定义,在上述头文件中有一下结构
这里写图片描述
person类中所定义的属性和方法,在内存中的存储方式就是通过Runtime的struct objc_class结构来定义,每一个类的实例在Runtime中都会用objc_class这个结构来表示,这也意味着所有对象也都包含了objc_class结构中所定义的属性。

isa的类型,就是objc_class这个结构的类型。isa所指向的结构正是这个类的属性和方法的定义;

再回到objc_msgSend函数中,它的第一个参数就是我们要发送消息的实例。
首先objc_msgSend函数会检测这个实例的isa属性,找到isa中定义的:
这里写图片描述
methodLists属性表示当前实例的方法列表,它是一个objc_method_list类型的结构:
这里写图片描述
method_count属性表示当前这个实例中方法的个数;

method_list的每一个元素又是一个objc_method类型的结构:
这里写图片描述
method_name是SEL类型,就是@selector(themeCK)这样的表达式所表示的类型;Selector,它其实是Runtime的一个数据结构,它代表一个方法的唯一标识。

第二个参数method_types,这个属性用一个字符串表示方法返回值类型以及每个参数的类型,并用@encode规则进行编码。

最后一个参数IMP类型,它表示这个Selector对应的函数的地址,真正要执行的函数地址。

总结:Objective-C中定义的所有类的方法在底层实现上就是一个函数。

三:消息分发流程
这里写图片描述
说了这么多再来回顾一下objc_msgSend函数:
这里写图片描述
解释:
这里写图片描述
消息分发的基本流程图:
这里写图片描述

四:方法实现
Objective-C中所有的方法调用,其实都会隐式的传递进来两个参数,在开发过程中Objective-C Runtime替我们完成了(1)self (2)_cmd,_cmd表示当前函数所对应的Selector。

五:消息缓存
实际上Objective-C 中调用一个方法需要两个过程,(1)通过消息分发找到对应的函数(注意这里是函数,在Runtime中只有函数Function)(2)然后再调用这个函数,并传递相应的参数。

实际上消息分发的过程是比较消耗性能的,需要进行一系列的查表操作,所以Objective-C Runtime对消息的分发建立了缓存机制。回顾一下objc_class结构的定义,是否还记得它定义了一个cache属性:
这里写图片描述
它的类型是objc_cache继续找到这个结构的定义:
这里写图片描述
实际上objc_cache维护了一个哈希表,使用Selector作为键,存储了缓存的函数列表。
这里写图片描述

六:消息分发的传递机制
比如我们在发送一个消息时,如果当前实例的方法列表没找到对应的函数怎么办,比如发送如下消息:
这里写图片描述
person类中确实没有定义description方法。对于这样的情况,Objective-C Runtime会继续查找它的父类,使用定义在objc_class结构中的super_class属性。
这里写图片描述
这里写图片描述
如果遍历完整个磊曾依然找不到对应的方法实现,默认情况下就会抛出异常:
这里写图片描述
不过这只是默认行为,其实这个异常可以不抛出的。我们完全在发送了一个并没有实现的消息的时候不让程序崩溃。这就是消息转发机制了。

七:消息转发机制
尽量避免这样的消息发送导致APP崩溃,比如可以使用respondsToSelector:方法在调用之前判断这个实例们能不能相应这个消息 。
这里写图片描述
在Runtime的整个消息传递中,我们还能在其他时机上处理这个事情,这就是Runtime的消息转发机制。
继承自NSObject的类可以覆盖这个方法:
这里写图片描述
同时还需要覆盖:
这里写图片描述
如果person不能相应sayHello这个消息,程序并不会马上崩溃,在这之前Runtime会调用person的forwardInvocation:方法。

forwardInvocation:接受一个类型为NSinvocaton的参数。NSinvocaton中存储了我们发送失败sayHello消息的详细信息。在这里我们把它转发到另一个实例上面。
这里写图片描述
这里写图片描述
这里写图片描述
除了可以通过forwardInvocation:来处理装法规则
,同样还可以忽略掉出错的消息。

八:直接发送消息
加入头文件
这里写图片描述

message.h中定义的objc_msgSend函数,并没有明确参数列表和返回值类型,所以我们需要强制转换一下,否则我们会遇到编译错误:
这里写图片描述

然后调用这个转换后的函数,并传入相应的参数:
这里写图片描述

这样编译通过,输出hello。

九:直接调用函数
我们通过调用objc_msgSend来发送消息,但它还是要经过消息分发的过程。这里绕过消息分发过程直接调用函数。

NSObject中定义了一个methodForSelector方法,可以得到Selector所对应的函数:
这里写图片描述
我们通过methodForSelector得到了sayHello函数的地址引用,这样就直接调用sayHello函数了,就绕过了Runtime的消息分发机制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值