消息机制是我见过runtime里面最常见的面试题,几乎问到runtime,必问消息机制
换句话说就是[self test]; 这个函数调用,是怎么找到底层函数列表的test函数,首先这段代码的底层是c++转成C++调用的大约是objc_msgSend(self,sel_registerName(test))这种形式,所以
第一步:消息发送阶段
objc_msgSend(self,sel_registerName(test)),给对应的类发送发送消息,前面的是类,后面的是函数最终结果是一个SEL,所以转化一下这个代码应该是objc_msgSend(class的类对象?,SEL)
然后进到lookUpImpOrForward,代码如下
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
IMP imp = nil;
Class curClass;
runtimeLock.assertUnlocked();
// Optimistic cache lookup
if (fastpath(behavior & LOOKUP_CACHE)) {
imp = cache_getImp(cls, sel);
if (imp) goto done_nolock;
}
// runtimeLock is held during isRealized and isInitialized checking
// to prevent races against concurrent realization.
// runtimeLock is held during method search to make
// method-lookup + cache-fill atomic with respect to method addition.
// Otherwise, a category could be added but ignored indefinitely because
// the cache was re-filled with the old value after the cache flush on
// behalf of the category.
runtimeLock.lock();
// We don't want people to be able to craft a binary blob that looks like
// a class but really isn't one and do a CFI attack.
//
// To make these harder we want to make sure this is a class that was
// either built into the binary or legitimately registered through
// objc_duplicateClass, objc_initializeClassPair or objc_allocateClassPair.
//
// TODO: this check is quite costly during process startup.
checkIsKnownClass(cls);
if (slowpath(!cls->isRealized())) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
// runtimeLock may have been dropped but is now locked again
}
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
// runtimeLock may have been dropped but is now locked again
// If sel == initialize, class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
runtimeLock.assertLocked();
curClass = cls;
// The code used to lookpu the class's cache again right after
// we take the lock but for the vast majority of the cases
// evidence shows this is a miss most of the time, hence a time loss.
//
// The only codepath calling into this without having performed some
// kind of cache lookup is class_getInstanceMethod().
for (unsigned attempts = unreasonableClassCount();;) {
// curClass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
imp = meth->imp;
goto done;
}
if (slowpath((curClass = curClass->superclass) == nil)) {
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = forward_imp;
break;
}
// Halt if there is a cycle in the superclass chain.
if (slowpath(--attempts == 0)) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
imp = cache_getImp(curClass, sel);
if (slowpath(imp == forward_imp)) {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
if (fastpath(imp)) {
// Found the method in a superclass. Cache it in this class.
goto done;
}
}
// No implementation found. Try method resolver once.
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
done:
log_and_fill_cache(cls, imp, sel, inst, curClass);
runtimeLock.unlock();
done_nolock:
if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
return nil;
}
return imp;
}
感兴趣的可以详细看看大致解释下,首先是从缓存列表取函数,如果得到了直接返回,然后是访问函数列表,然后访问superclass的cache,然后是函数列表,如果还是没找到,则进入动态方法解析阶段
第二步:动态方法解析
第一步走完了即使没找到也不会直接崩溃,会进入动态方法解析的阶段
动态方法解析首先会判断是否还有父类,有的话继续访问cache散列表 + 方法列表,直到没有父类为止,源码如下
static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
runtimeLock.unlock();
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
resolveInstanceMethod(inst, sel, cls);
}
else {
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
resolveClassMethod(inst, sel, cls);
if (!lookUpImpOrNil(inst, sel, cls)) {
resolveInstanceMethod(inst, sel, cls);
}
}
// chances are that calling the resolver have populated the cache
// so attempt using it
return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
}
可以看出上面的代码在递归的同时,访问了一个叫resolveInstanceMethod的函数
/***********************************************************************
* resolveInstanceMethod
* Call +resolveInstanceMethod, looking for a method to be added to class cls.
* cls may be a metaclass or a non-meta class.
* Does not check if the method already exists.
**********************************************************************/
static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
runtimeLock.assertUnlocked();
ASSERT(cls->isRealized());
SEL resolve_sel = @selector(resolveInstanceMethod:);
if (!lookUpImpOrNil(cls, resolve_sel, cls->ISA())) {
// Resolver not implemented.
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, resolve_sel, sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveInstanceMethod adds to self a.k.a. cls
IMP imp = lookUpImpOrNil(inst, sel, cls);
if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
这就是resolveInstanceMethod函数,这个函数官方给的解释是Call +resolveInstanceMethod, looking for a method to be added to class cls
调用一个叫resolveInstanceMethod的类方法,找一个函数去添加进类和类的实例
+(BOOL)resolveInstanceMethod:(SEL)sel
{
}
+(BOOL)resolveClassMethod:(SEL)sel
{
}
实际有两个,一个类方法,一个实例方法,其实差不多,只说实例方法
id p = [Public sharePublic];
[p test];
return 0;
+(BOOL)resolveInstanceMethod:(SEL)sel
{
if(sel == @selector(test))
{
Method method = class_getInstanceMethod(self, @selector(addTest));
class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
}
return YES;
}
-(void)addTest
{
NSLog(@"%s",__func__);
}
当然这里面是已有方法,而不是手动创建的动态方法,动态创建想单独写一个来介绍各种参数的作用
第三步:消息转发
如果不处理方法动态解析就进入了最后的消息转发阶段
这个阶段是不开源的,只能通过汇编来查看,最后是进入了?___forwarding___这个函数里面,网上有很多伪代码,就是分析出这个的具体实现,但具体实现还是不清楚,-(id)forwardingTargetForSelector:(SEL)aSelector最后是到这里,这是能操作的地方,这个函数的目的就是消息转发(废话),怎么转发呢,
比如我的上述代码是往Public里面发消息去找test函数,但Public包括它的父类NSObjcet都没有,前面的动态解析操作也没做,那么久会在这个方法里面要求指定一个类,把消息转发出去,具体代码实现:
首先定义个Person类,然后实现test函数
#import "Person.h"
@implementation Person
-(void)test
{
NSLog(@"%s",__func__);
}
@end
然后在Public.m里面加上这个:
-(id)forwardingTargetForSelector:(SEL)aSelector
{
if(@selector(test) == aSelector)
{
return [[Person alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
把原本自己类或者父类需要实现,但没实现的函数,丢给其它类处理,就叫做消息转发
最后输出的结果就是-[Person test]
确实是转发出去了
?总结起来就是上图,不过没有消息转发,在动态解析之后
|