众所周知,Objective-C 中的方法调用跟C语言中的函数调用很不一样,OC 中的方法调用称为消息发送。例如,[self callMethod]
称为对 self 对象发送callMethod
的消息。正是这种消息发送机制,让 OC 有一个很神奇的特性,对nil
进行方法调用不会导致崩溃。
本文粗略讲述下OC的方法调用,介绍下在 OC 中通过 selector 的形式调用方法,实际上都发生了什么。
performSelector
先看下 OC runtime 的源码对于 performSelector 的实现。
1 | - (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 { |
原来 performSelector 的实现如此简单,那么完全可以自己对 NSObject 做一个扩展,支持调用需要三个参数的 selector。
1 | - (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 withObject:(id)obj3 { |
消息发送流程
performSelector
实际上是调用了objc_msgSend
。objc_msgSend
并没有源码可以给我们参考。不过我们看下NSObject
中的几个跟消息发送相关的方法。
+ (BOOL)resolveClassMethod:(SEL)sel
和+ (BOOL)resolveInstanceMethod:(SEL)sel
在方法中,允许通过调用class_addMethod
来动态的为sel
添加一个实现。如果动态添加了实现,则返回YES。- (id)forwardingTargetForSelector:(SEL)aSelector
当从self
中找不到aSelector
对应的实现时,调用此方法。此方法如果返回一个非nil
且非self
的对象,则会把该对象作为新的消息接受者。在该对象中寻找对应的实现。- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
返回aSelector
的方法签名。方法签名和 Type Encoding 会另开一篇博客来介绍。- (void)forwardInvocation:(NSInvocation *)anInvocation
当对self
发送一个unrecoginzed
的消息时,会创建一个NSInvocation
,并调用这个方法。允许在这个方法中,通过[anInvocation invokeWithTarget:otherSelf];
的方式进行消息转发。
具体这几个方法之间的关系和调用顺序如下:
其他形式
前面的介绍中,都是以 performSelector 来执行为基础的。那么,直接方法调用的流程跟与 selector 调用是一样的吗?
假设有这个一个方法:- (void)methodDirectInvoke:(int)param;
。那么通过clang命令,[self methodDirectInvoke:1];
被转化为:
1 | ((void (*)(id, SEL, int))(void *)objc_msgSend)((id)self, sel_registerName("methodDirectInvoke:"), 1); |
由此可见,所有的方法调用都会被转化为objc_msgSend
消息发送,因此直接的方法调用流程与selector
调用是一致的。