美文网首页
IMP和SEL以及具体执行的操作

IMP和SEL以及具体执行的操作

作者: natewang | 来源:发表于2018-06-24 16:40 被阅读27次

    Selector

    定义:typedef struct objc_selector *SEL

    SEL selA = @selector(setString:);
    

    Implementation(IMP):

    定义:typedef id (*IMP)(id, SEL, ...)

    代表函数指针,即函数执行的入口。该函数使用标准的 C 调用。第一个参数指向 self(它代表当前类实例的地址,如果是类则指向的是它的元类),作为消息的接受者;第二个参数代表方法的选择子;... 代表可选参数,前面的 id 代表返回值。

    Method

    定义:typedef struct objc_method *Method

    /// Method
    struct objc_method {
        SEL method_name; 
        char *method_types;
        IMP method_imp;
    };
    

    从上面可以看出,sel是method的标识,IMP是method的实现。

    一个类有自己的方法表,表中存有method,每次的方法调用都是从这个表中查找,查找到sel,自然就找到IMP。

    struct objc_class {
    
            Class isa  OBJC_ISA_AVAILABILITY;
    
    
            #if !__OBJC2__
    
            Class super_class                       OBJC2_UNAVAILABLE;  // 父类
    
            const char *name                        OBJC2_UNAVAILABLE;  // 类名
    
            long version                            OBJC2_UNAVAILABLE;  // 类的版本信息,默认为0
    
            long info                               OBJC2_UNAVAILABLE;  // 类信息,供运行期使用的一些位标识
    
            long instance_size                      OBJC2_UNAVAILABLE;  // 该类的实例变量大小
    
            struct objc_ivar_list *ivars            OBJC2_UNAVAILABLE;  // 该类的成员变量链表
    
            struct objc_method_list **methodLists   OBJC2_UNAVAILABLE;  // 方法定义的链表
    
            struct objc_cache *cache                OBJC2_UNAVAILABLE;  // 方法缓存
    
            struct objc_protocol_list *protocols    OBJC2_UNAVAILABLE;  // 协议链表
    
            #endif
    
    } OBJC2_UNAVAILABLE;
    
    

    这个oc的类结构体定义, methodLists 就是存放method的表。

    id objc_msgSend(id self, SEL op, ...) {
        if (!self) return nil;
        IMP imp = class_getMethodImplementation(self->isa, SEL op);
        imp(self, op, ...); //调用这个函数,伪代码...
    }
     
    //查找IMP
    IMP class_getMethodImplementation(Class cls, SEL sel) {
        if (!cls || !sel) return nil;
        IMP imp = lookUpImpOrNil(cls, sel);
        if (!imp) return _objc_msgForward; //这个是用于消息转发的
        return imp;
    }
     
    IMP lookUpImpOrNil(Class cls, SEL sel) {
        if (!cls->initialize()) {
            _class_initialize(cls);
        }
     
        Class curClass = cls;
        IMP imp = nil;
        do { //先查缓存,缓存没有时重建,仍旧没有则向父类查询
            if (!curClass) break;
            if (!curClass->cache) fill_cache(cls, curClass);
            imp = cache_getImp(curClass, sel);
            if (imp) break;
        } while (curClass = curClass->superclass);
     
        return imp;
    }
    

    如果找不到就会进入方法转发阶段

    resolveInstanceMethod:
    forwardingTargetForSelector:
    methodSignatureForSelector:
    
    doesNotRecognizeSelector: 
    
    

    1.调用resolveInstanceMethod:方法,允许用户在此时为该Class动态添加实现。如果有实现了,则调用并返回。如果仍没实现,继续下面的动作。

    2.调用forwardingTargetForSelector:方法,尝试找到一个能响应该消息的对象。如果获取到,则直接转发给它。如果返回了nil,继续下面的动作。

    3.调用methodSignatureForSelector:方法,尝试获得一个方法签名。如果获取不到,则直接调用doesNotRecognizeSelector抛出异常。

    4.调用forwardInvocation:方法,将地3步获取到的方法签名包装成Invocation传入,如何处理就在这里面了。

    相关文章

      网友评论

          本文标题:IMP和SEL以及具体执行的操作

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