objc_msgSend方法调用流程

这个方法做的事情不少,举个栗子:

[self printMessageWithString:@"Hello World!"];

这句语句被编译成这样:

objc_msgSend(self,@selector(printMessageWithString:),@"Hello World!");

这个方法先去查找 self 这个对象或者其父类是否响应
@selector(printMessageWithString:),如果从这个类的方法分发表或
者 cache 里面找到了,就调用它对应的函数指针。如果找不到,那就会执行一些其他的东西。步骤如下:
检测这个 selector 是不是要忽略的。比如 Mac OS X 开发,有了垃圾回收就不理会 retain, release 这些函数了。
检测这个 target 是不是 nil 对象。ObjC 的特性是允许对一个 nil 对象执行任何一个方法不会 Crash,因为会被忽略掉。
如果上面两个都过了,那就开始查找这个类的 IMP,先从 cache 里面找,完了找得到就跳到对应的函数去执行。
如果 cache 找不到就找一下方法分发表。
如果还找不到就要开始消息转发逻辑了。
在编译的时候,你定义的方法比如:

-(int)doComputeWithNum:(int)aNum

会编译成:

int aClass_doComputeWithNum(aClass *self,SEL _cmd,int aNum)

然后由 runtime 去调用指向你的这个方法的函数指针。那么之前我们说你发起消息其实不是对方法的直接调用,其实 Cocoa 还是提供了可以直接调用的方法的:

// 首先定义一个 C 语言的函数指针
int (computeNum *)(id,SEL,int);
// 使用 methodForSelector 方法获取对应与该 selector 的杉树指针,跟 objc_msgSend 方法拿到的是一样的
// **methodForSelector 这个方法是 Cocoa 提供的,不是 ObjC runtime 库提供的**
computeNum = (int (*)(id,SEL,int))[target methodForSelector:@selector(doComputeWithNum:)];
// 现在可以直接调用该函数了,跟调用 C 函数是一样的
computeNum(obj,@selector(doComputeWithNum:),aNum);

参考自:理解 Objective-C Runtime

你可能感兴趣的:(objc_msgSend方法调用流程)