初步一个描述:
1、Objective-C为我们维护了一个巨大的选择子表
2、在使用@selector()时会从这个选择子表中根据选择子的名字查找对应的SEL。如果没有找到,则会生成一个SEL并添加到表中
3、在编译期间会扫描全部的头文件和实现文件将其中的方法以及使用@selector()生成的选择子加入到选择子表中
Objective-C中objc_msgSend的实现并没有开源,它只存在于message.h这个头文件中。
- @note When it encounters a method call, the compiler generates a call to one of the
- functions \c objc_msgSend, \c objc_msgSend_stret, \c objc_msgSendSuper, or \c objc_msgSendSuper_stret.
- Messages sent to an object’s superclass (using the \c super keyword) are sent using \c objc_msgSendSuper;
- other messages are sent using \c objc_msgSend. Methods that have data structures as return values
- are sent using \c objc_msgSendSuper_stret and \c objc_msgSend_stret.
OBJC_EXPORT id _Nullable
objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
在这个头文件的注释中对消息发送的一系列方法解释得非常清楚:
当编译器遇到一个方法调用时,它会将方法的调用翻译成以下函数中的一个objc_msgSend、objc_msgSend_stret、objc_msgSendSuper和objc_msgSendSuper_stret。发送给对象的父类的消息会使用objc_msgSendSuper有数据结构作为返回值的方法会使用objc_msgSendSuper_stret或objc_msgSend_stret其它的消息都是使用objc_msgSend发送的
在这篇文章中,我们只会对消息发送的过程进行分析,而不会对上述消息发送方法的区别进行分析,默认都使用objc_msgSend函数。
1.objc_msgSend调用栈
调用栈:objc_msgSend ---> class_lookupMethodAndLoadCache3 ---> lookUpImpOrForward
lookUpImpOrForward函数会返回一个IMP指针,如果这个指针不为空的话,则直接调用指针,否则,会调用doesNotRecognizeSelector
函数,这个函数默认的实现是,抛出异常,程序crash。
2. lookUpImpOrForward的实现
_class_lookupMethodAndLoadCache3函数实现:主要是调用了lookUpImpOrForward
函数
IMP_class_lookupMethodAndLoadCache3(idobj,SELsel,Classcls)
{
return lookUpImpOrForward(cls,sel,obj,
YES/*initialize*/,NO/*cache*/,YES/*resolver*/);
}
接下来,我们继续看lookUpImpOrForward
函数的实现:
由于实现的查找方法lookUpImpOrForward涉及很多函数的调用,所以我们将它分成以下几个部分来分析:
1、无锁的缓存查找
2、如果类没有实现(isRealized)或者初始化(isInitialized),实现或者初始化类
3、加锁
4、缓存以及当前类中方法的查找
5、尝试查找父类的缓存以及方法列表
6、没有找到实现,尝试方法解析器
7、进行消息转发
8、解锁、返回实现
无锁的缓存查找
IMP imp = nil;
bool triedResolver = NO;
runtimeLock.assertUnlocked();
// Optimistic cache lookup
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
类的实现和初始化
if(!cls->isRealized()){
rwlock_writer_tlock(runtimeLock);
realizeClass(cls);
}
if(initialize&&!cls->isInitialized()){
_class_initialize(_class_getNonMetaClass(cls,inst));
}
在Objective-C运行时初始化的过程中会对其中的类进行第一次初始化也就是执行realizeClass方法,为类分配可读写结构体class_rw_t的空间,并返回正确的类结构体。
而_class_initialize方法会调用类的initialize方法,这就是Object-C中的load和initialize方法的过程。
加锁
runtimeLock.read();
在当前类中查找实现
imp = cache_getImp(cls,sel);
if(imp)goto done;
实现很简单,先调用了cache_getImp从某个类的cache属性中获取选择子对应的实现。
如果没有找到,则会去找method lists属性中去查找:
// Try this class's method lists.
{
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
}
getMethodNoSuper_nolock
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
runtimeLock.assertLocked();
assert(cls->isRealized());
// fixme nil cls?
// fixme nil sel?
for (auto mlists = cls->data()->methods.beginLists(),
end = cls->data()->methods.endLists();
mlists != end;
++mlists)
{
method_t *m = search_method_list(*mlists, sel);
if (m) return m;
}
return nil;
}
因为类中数据的方法列表methods是一个二维数组method_array_t,写一个for循环遍历整个方法列表.
search_method_list
static method_t *search_method_list(const method_list_t *mlist, SEL sel)
{
int methodListIsFixedUp = mlist->isFixedUp();
int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) {
return findMethodInSortedMethodList(sel, mlist);
} else {
// Linear search of unsorted method list
for (auto& meth : *mlist) {
if (meth.name == sel) return &meth;
}
}
#if DEBUG
// sanity-check negative results
if (mlist->isFixedUp()) {
for (auto& meth : *mlist) {
if (meth.name == sel) {
_objc_fatal("linear search worked when binary search did not");
}
}
}
#endif
return nil;
}
findMethodInSortedMethodList方法对有序方法列表进行线性探测,返回方法结构体method_t。
如果在这里找到了方法的实现,通过log_and_fill_cache ---> cache_fill ---> cache_fill_nolock将它加入类的缓存中.
在父类中寻找实现
这一部分与上面在本类里边查找的实现基本上是一样的,只是多了一个循环用来判断根类。
// Try superclass caches and method lists.
{
unsigned attempts = unreasonableClassCount();
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
{
// Halt if there is a cycle in the superclass chain.
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// Found the method in a superclass. Cache it in this class.
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
}
// Superclass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
与当前类寻找实现的区别是:在父类中寻找到的_objc_msgForward_impcache实现会交给当前类来处理。
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
方法决议
选择子在当前类和父类中都没有找到实现,就进入了方法决议(methodresolve)的过程:
// No implementation found. Try method resolver once.
if (resolver && !triedResolver) {
runtimeLock.unlockRead();
_class_resolveMethod(cls, sel, inst);
runtimeLock.read();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;
}
这部分代码调用_class_resolveMethod来解析没有找到实现的方法。
void_class_resolveMethod(Classcls,SELsel,idinst)
{
if(!cls->isMetaClass()){
_class_resolveInstanceMethod(cls,sel,inst);
}
else{
_class_resolveClassMethod(cls,sel,inst);
if(!lookUpImpOrNil(cls,sel,inst,
NO/*initialize*/,YES/*cache*/,NO/*resolver*/))
{
_class_resolveInstanceMethod(cls,sel,inst);
}
}
}
根据当前的类是不是元类在_class_resolveInstanceMethod和_class_resolveClassMethod中选择一个进行调用,这两个方法的实现其实就是判断当前类是否实现了resolveInstanceMethod:或者resolveClassMethod:方法,然后用objc_msgSend执行上述方法,并传入需要决议的选择子。
消息转发
在缓存、当前类、父类以及resolveInstanceMethod:都没有解决实现查找的问题时,Objective-C还为我们提供了最后一次翻身的机会,进行方法转发:
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
这个方法的实现其实就是判断当前类是否实现了forwardInvocation:,然后用objc_msgSend执行上述方法,并传入需要决议的选择子。
最后
解锁,返回实现的IMP。