OC消息发送机制

OC的方法调用都是通过消息发送这种机制来实现的。当调用一个实例方法或者类方法时,底层实现是实例对象或者类对象调用objc_msgSend函数。先看个例子:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    UIView *view = [UIView new];
    view.backgroundColor = [UIColor redColor];
}

// 下面是通过clang转换后的代码
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));

    UIView *view = ((UIView *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("UIView"), sel_registerName("new"));
    ((void (*)(id, SEL, UIColor * _Nullable))(void *)objc_msgSend)((id)view, sel_registerName("setBackgroundColor:"), (UIColor * _Nullable)__null);

}

// 这是objc_msgSend函数的定义
objc_msgSend(void /* id self, SEL op, ... */ )

上面的代码可以看到,类方法和实例方法的调用都是通过调用objc_msgSend实现的。

OC是一门动态语言,调用的方法只要在.h文件声明了就能编译通过,但方法具体的调用要到运行时才能确定。objc_msgSend是用汇编实现的, objc_msgSend会再调用__class_lookupMethodAndLoadCache3函数, __class_lookupMethodAndLoadCache3函数调用lookUpImpOrForward函数,源码如下:

IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    Class curClass;
    IMP imp = nil;
    Method meth;
    bool triedResolver = NO;
    runtimeLock.assertUnlocked();

    // Optimistic cache lookup  缓存中查找
    if (cache) {
        imp = cache_getImp(cls, sel);
        if (imp) return imp; //找到直接返回
    }
    if (!cls->isRealized()) {
        rwlock_writer_t lock(runtimeLock);
        realizeClass(cls);
    }
    if (initialize  &&  !cls->isInitialized()) {
        _class_initialize (_class_getNonMetaClass(cls, inst));
    }

 retry:
    runtimeLock.read();

    // Ignore GC selectors
    if (ignoreSelector(sel)) {
        imp = _objc_ignored_method;
        cache_fill(cls, sel, imp, inst);
        goto done;
    }

    // Try this class's cache. 不知道什么原因再次查缓存
    imp = cache_getImp(cls, sel);
    if (imp) goto done;

    // Try this class's method lists. //类的方法列表中查找
    meth = getMethodNoSuper_nolock(cls, sel);
    if (meth) {
        log_and_fill_cache(cls, meth->imp, sel, inst, cls);//找到缓存的类的缓存列表
        imp = meth->imp;
        goto done;
    }

    // Try superclass caches and method lists.//循环查询父类的缓存和方法列表中,一直找到NSObject为止
    curClass = cls;
    while ((curClass = curClass->superclass)) {
        // 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.
        meth = getMethodNoSuper_nolock(curClass, sel);
        if (meth) {
            log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
            imp = meth->imp;
            goto done;
        }
    }

    // No implementation found. Try method resolver once.
    if (resolver  &&  !triedResolver) {
        runtimeLock.unlockRead();
        _class_resolveMethod(cls, sel, inst);
        // 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;
    }

    // No implementation found, and method resolver didn't help. 
    // Use forwarding.
    imp = (IMP)_objc_msgForward_impcache;
    cache_fill(cls, sel, imp, inst);

 done:
    runtimeLock.unlockRead();
    // paranoia: look for ignored selectors with non-ignored implementations
    assert(!(ignoreSelector(sel)  &&  imp != (IMP)&_objc_ignored_method));
    // paranoia: never let uncached leak out
    assert(imp != _objc_msgSend_uncached_impcache);
    return imp;
}

从上面的源码中可以看出,一个方法的调用可能会经过三个阶段:

  • 消息查找阶段
    很多人称为消息发送阶段,我自己更习惯称消息的查找阶段。在Class的结构及方法缓存中说过,方法的调用会先去类的缓存列表区查找函数的实现,从上面的源码也证实了这一点,如果在类的缓存中找到函数实现直接返回该实现。

    如果缓存中没有,接着去类的方法列表中查找,找后缓存到类中后返回该函数。

    如果类方法列表也没有找到,接着去循环查询父类的缓存和方法列表,直至找NSObject为止。同样的,找后缓存到类中后返回该函数。如果都没有找到对应的方法实现,将进入第二个阶段,消息的动态解析;

  • 消息动态解析阶段
    之所以叫动态解析阶段,是因为我们可以在这个阶段为类或元类添加目标方法的实现。首先调用函数_class_resolveMethod

void _class_resolveMethod(Class cls, SEL sel, id inst)
{
    if (! cls->isMetaClass()) {
        // try [cls resolveInstanceMethod:sel]
        _class_resolveInstanceMethod(cls, sel, inst);
    } 
    else {
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        _class_resolveClassMethod(cls, sel, inst);
        if (!lookUpImpOrNil(cls, sel, inst, 
                            NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
        {
            _class_resolveInstanceMethod(cls, sel, inst);
        }
    }
}

根据cls是类还是元类(也即是调用的是实例方法还是类方法)分别调用函数_class_resolveInstanceMethod、_class_resolveClassMethod添加目标方法的实现。这两个函数的实现大致相同,贴出_class_resolveInstanceMethod的源码:

static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
    if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls, 
                         NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
    {
        // Resolver not implemented.
        return;
    }

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, SEL_resolveInstanceMethod, 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(cls, sel, inst, 
                             NO/*initialize*/, YES/*cache*/, NO/*resolver*/);

    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中为类添加对应的方法实现函数直接return。不论是否填加了实现,都会把结果缓存起来,避免以后调用再次触发方法的动态解析。如果在动态方法解析阶段,也没有为类添加方法实现,最后将进入消息转发阶段;

  • 消息转发阶段
    消息转发的具体的实现并没有开源,但是国外的开发者者根据调试的结果给出了消息转发的伪代码,如下:
// 消息转发伪代码
int __forwarding__(void *frameStackPointer, int isStret) {
    id receiver = *(id *)frameStackPointer;
    SEL sel = *(SEL *)(frameStackPointer + 8);
    const char *selName = sel_getName(sel);
    Class receiverClass = object_getClass(receiver);

    // 调用 forwardingTargetForSelector:
    if (class_respondsToSelector(receiverClass, @selector(forwardingTargetForSelector:))) {
        id forwardingTarget = [receiver forwardingTargetForSelector:sel];
        if (forwardingTarget && forwardingTarget != receiver) {
            if (isStret == 1) {
                int ret;
                objc_msgSend_stret(&ret,forwardingTarget, sel, ...);
                return ret;
            }
            return objc_msgSend(forwardingTarget, sel, ...);
        }
    }

    // 僵尸对象
    const char *className = class_getName(receiverClass);
    const char *zombiePrefix = "_NSZombie_";
    size_t prefixLen = strlen(zombiePrefix); // 0xa
    if (strncmp(className, zombiePrefix, prefixLen) == 0) {
        CFLog(kCFLogLevelError,
              @"*** -[%s %s]: message sent to deallocated instance %p",
              className + prefixLen,
              selName,
              receiver);
        
    }

    // 调用 methodSignatureForSelector 获取方法签名后再调用 forwardInvocation
    if (class_respondsToSelector(receiverClass, @selector(methodSignatureForSelector:))) {
        NSMethodSignature *methodSignature = [receiver methodSignatureForSelector:sel];
        if (methodSignature) {
            BOOL signatureIsStret = [methodSignature _frameDescriptor]->returnArgInfo.flags.isStruct;
            if (signatureIsStret != isStret) {
                CFLog(kCFLogLevelWarning ,
                      @"*** NSForwarding: warning: method signature and compiler disagree on struct-return-edness of '%s'.  Signature thinks it does%s return a struct, and compiler thinks it does%s.",
                      selName,
                      signatureIsStret ? "" : not,
                      isStret ? "" : not);
            }
            if (class_respondsToSelector(receiverClass, @selector(forwardInvocation:))) {
                NSInvocation *invocation = [NSInvocation _invocationWithMethodSignature:methodSignature frame:frameStackPointer];

                [receiver forwardInvocation:invocation];

                void *returnValue = NULL;
                [invocation getReturnValue:&value];
                return returnValue;
            } else {
                CFLog(kCFLogLevelWarning ,
                      @"*** NSForwarding: warning: object %p of class '%s' does not implement forwardInvocation: -- dropping message",
                      receiver,
                      className);
                return 0;
            }
        }
    }

    SEL *registeredSel = sel_getUid(selName);

    // selector 是否已经在 Runtime 注册过
    if (sel != registeredSel) {
        CFLog(kCFLogLevelWarning ,
              @"*** NSForwarding: warning: selector (%p) for message '%s' does not match selector known to Objective C runtime (%p)-- abort",
              sel,
              selName,
              registeredSel);
    } // doesNotRecognizeSelector
    else if (class_respondsToSelector(receiverClass,@selector(doesNotRecognizeSelector:))) {
        [receiver doesNotRecognizeSelector:sel];
    }
    else {
        CFLog(kCFLogLevelWarning ,
              @"*** NSForwarding: warning: object %p of class '%s' does not implement doesNotRecognizeSelector: -- abort",
              receiver,
              className);
    }

    // The point of no return.
    kill(getpid(), 9);
}

从上面的代码可以看出消息转发阶段的整体流程:

  • 首先调用forwardingTargetForSelector方法,我们可以在这个方法里返回一个实现了目标SEL的对象,那么最终会调用这个对象的方法实现。如果返回的对象没有实现目标函数,仍会报“unrecognized selector ...”的错误。

  • 如果forwardingTargetForSelector中没有做处理,接着会调用methodSignatureForSelector方法,我们可以在这个方法里返回一个方法签名,返回方法签名后会调用forwardInvocation方法,在forwardInvocation方法里,我们可以任意的实现目标方法的调用。只要methodSignatureForSelector方法里返回了方法签名,forwardInvocation中的实现,我们可以任意处理,甚至不做任何处理程序都不会崩溃。

举个实例,新建Person类,只在.h文件中声明了两个方法classTest、instanceTest,但未实现。Student类声明的同样的方法,并在.m文件实现了这两个方法:

@interface Person : NSObject

+ (void)classTest;
- (void)instanceTest;
@end

@interface Student : NSObject
+ (void)classTest;
- (void)instanceTest;
@end

#import "Student.h"

@implementation Student

+ (void)classTest {
    NSLog(@"%s",__func__);
}
- (void)instanceTest {
    NSLog(@"%s",__func__);
}

@end

在Person的.m文件中没有这两个实现方法,但我们在forwardingTargetForSelector方法中返回一个实现了classTest、instanceTest的Student类对象和实例对象

#import "Person.h"
#import 
#import "Student.h"

@implementation Person

- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(instanceTest)) {
        return [Student new];
    }
    return [super forwardingTargetForSelector:aSelector];
}

+ (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(classTest)) {
        return [Student class];
    }
    return [super forwardingTargetForSelector:aSelector];
}

@end

上面代码运行打印结果如下:
-[Student instanceTest]
+[Student classTest]

在Person的.m文件中没有这两个实现方法,但我们在methodSignatureForSelector和forwardInvocation方法中做了处理:


#import "Person.h"
#import 
#import "Student.h"

@implementation Person


- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(instanceTest)) {
        NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:"];
        return signature;
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    [anInvocation invokeWithTarget:[Student new]];
}


+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(classTest)) {
        NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:"];
        return signature;
    }
    return [super methodSignatureForSelector:aSelector];
}

+ (void)forwardInvocation:(NSInvocation *)anInvocation {

    [anInvocation invokeWithTarget:[Student class]];
}

@end

上面代码运行打印结果如下:
-[Student instanceTest]
+[Student classTest]

super的实现

在一段代码中[super viewDidLoad]这句代码通过clang编译后的代码如下:

 ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self,   (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));

去掉强制转换简化后的结构如下:

 objc_msgSendSuper(
            {
             (id)self,                           
             (id)class_getSuperclass(objc_getClass("ViewController"))
             }, 
             sel_registerName("viewDidLoad")
 );

super的底层结构如下:

struct objc_super {
    __unsafe_unretained id receiver;
    __unsafe_unretained Class super_class;
};

super底层转换成调用objc_msgSendSuper函数,这个函数有两个参数,第一个参数为objc_super类型的对象,第二个参数是方法选择器;

下面是runtime源码中objc_msgSendSuper的定义

/** 
 * Sends a message with a simple return value to the superclass of an instance of a class.
   (向实例对象的类的父类发送消息,并返回一个值) 
 * 
 * @param super A pointer to an \c objc_super data structure. Pass values identifying the 
 *  context the message was sent to, including the instance of the class that is to receive the
 *  message and the superclass at which to start searching for the method implementation.
  
    (super 是一个指向objc_super类型的对象的指针,根据所传的值确定消息发送具体来龙去脉,类的实例对象是消息接受者,superclass是指我们要从哪里开始查找方法的实现)

 * @param op A pointer of type SEL. Pass the selector of the method that will handle the message.
 * @param ...
 *   A variable argument list containing the arguments to the method.
 * 
 * @return The return value of the method identified by \e op.
 * 
 * @see objc_msgSend
 */
OBJC_EXPORT id _Nullable
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

我给注释做了一些翻译,结合注释我们可以知道[super viewDidLoad]这句的代码的意思是:

  • ViewController的实例( (id)self )是消息接受者;
  • 从ViewController的父类( (id)class_getSuperclass(objc_getClass("ViewController")) )开始查找消息sel_registerName("viewDidLoad")的实现。

这里两个重点:消息的接受者是当前类的实例对象,从父类而不是当前类开始查找消息的实现;

你可能感兴趣的:(OC消息发送机制)