OC消息发送机制

作者: YY_Lee | 来源:发表于2019-02-25 18:04 被阅读5次

    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);
            <breakpoint-interrupt>
        }
    
        // 调用 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 <objc/runtime.h>
    #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 <objc/runtime.h>
    #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消息发送机制

        本文链接:https://www.haomeiwen.com/subject/rhciyqtx.html