0%

iOS教程(二)消息发送

众所周知,Objective-C 中的方法调用跟C语言中的函数调用很不一样,OC 中的方法调用称为消息发送。例如,[self callMethod]称为对 self 对象发送callMethod的消息。正是这种消息发送机制,让 OC 有一个很神奇的特性,对nil进行方法调用不会导致崩溃。

本文粗略讲述下OC的方法调用,介绍下在 OC 中通过 selector 的形式调用方法,实际上都发生了什么。

performSelector

先看下 OC runtime 的源码对于 performSelector 的实现。

1
2
3
4
- (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 {
if (!sel) [self doesNotRecognizeSelector:sel];
return ((id(*)(id, SEL, id, id))objc_msgSend)(self, sel, obj1, obj2);
}

原来 performSelector 的实现如此简单,那么完全可以自己对 NSObject 做一个扩展,支持调用需要三个参数的 selector。

1
2
3
4
- (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 withObject:(id)obj3 {
if (!sel) [self doesNotRecognizeSelector:sel];
return ((id(*)(id, SEL, id, id, id))objc_msgSend)(self, sel, obj1, obj2, obj3);
}

消息发送流程

performSelector实际上是调用了objc_msgSendobjc_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];的方式进行消息转发。

具体这几个方法之间的关系和调用顺序如下:
method-resolve

其他形式

前面的介绍中,都是以 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调用是一致的。