一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第26天,点击查看活动详情。
前言
我们在第一天学习Objective-C这一门语言的时候,就被告知这是一门动态语言。
C这样的编译语言,在编译阶段就确定了所有函数的调用链,如果函数没有被实现,编译就根本不过了。而基于动态语言的特性,在编译期间,我们无法确认程序在运行时要调用哪一个函数,某一个未被实现的函数是否会在运行时被实现。
这样就可能会出现运行时发现调用的函数根本不存在的尴尬,这也就是我们收到unrecognized selector sent to XXX这样的崩溃的原因了(动态语言也有让人心累的地方,手动叹气)。\
消息转发流程
我们知道在我们调用某一个方法之后,最终调用的是objc_msgSend()这样一个方法,发送消息(selector)给消息接收者(receiver)。这个方法会根据OC的消息发送机制在receiver中查找selector。如果没有查找到,就会出现上述的运行时调用了未实现的函数的尴尬局面了。
不过为了缓解这种尴尬,我们还有机会来挣扎。这挣扎机会就是消息转发流程。
消息转发流程包含以下3个步骤:
-
动态方法解析:
resolveInstanceMethod:和resolveClassMethod: -
消息转发
- 快速转发:
forwardingTargetForSelector: - 常规转发:
methodSignatureForSelector:和forwardInvocation:
- 快速转发:
消息转发流程是以动态方法解析、消息快速转发、消息常规转发这样的顺序来执行的。如果其中任意一个步骤能使消息被执行,那么就不会出现unrecognized selector sent to XXX的崩溃。
动态方法解析
resolveInstanceMethod:这个方法的作用是动态地为selector提供一个实例方法的实现。而resolveClassMethod:则是提供一个类方法的实现。
所以我们可以在这两个方法中,为对象添加方法的实现,再返回YES告诉已经为selector添加了实现。这样就会重新在对象上查找方法,找到我们新添加的方法后就直接调用。从而避免掉unrecognized selector sent to XXX。
需要注意的是: 这两个方法会响应respondsToSelector:和instancesRespondToSelector:。
消息快速转发
forwardingTargetForSelector:的作用是将消息转发给其它对象去处理。
我们可以在这个方法中,返回一个对象,让这个对象来响应消息。
需要注意的是: 如果在这个方法中返回self或nil,则表示没有可响应的目标。
消息常规转发
forwardInvocation:的作用也是将消息转发给其它对象。不过与 消息快速转发 不同的是该方法需要手动的创建一个NSInvocation对象,并手动地将新消息发送给新的接收者。
很显然,这种方式会比 消息快速转发 付出更大的消耗。