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

2018-06-24  本文已影响27人  natewang

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传入,如何处理就在这里面了。

上一篇下一篇

猜你喜欢

热点阅读