美文网首页
Runtime-开篇

Runtime-开篇

作者: 无题007 | 来源:发表于2017-07-17 21:26 被阅读21次

    关于Class和Id

    当你写一个Class去看系统的API时:
    typedef struct objc_class *Class; 同时你会发现有一个这个东西typedef struct objc_object *id;
    会发现id是一个结构体,并且里面只有一个Class类型的指针isa:

    struct objc_object {
    Class isa OBJC_ISA_AVAILABILITY;
    };

    当你再去看Class的时候会发现它也是系统定义的一个结构体,只不过它的结构复杂。

    • 在OC中,所有的类其实是一个对象,我们可以认为id中的isa指针指向的是一个类对象,并且在Class结构体中的isa指针指向元类。
    • 在Runtime中,以obj_开头的方法大多是针对id类型的对象进行操作,而以class_开头的方法主要是针对Class类型的对象进行操作。
    • Runtime的以class_开头的方法是针对与Class结构体的各个元素进行操作的。

    元类

    objc_object结构体中,有一个objc_class类型的指针,但在objc_class结构体中又有一个objc_class指针,这就引入了元类的概念。
    首先,元类是类对象的类,同样也就是一个对象,它的结构也是objc_class结构。为了不让这种结构无限延伸下去,OC让所有的元类的isa指针指向基类的元类,以此作为它们的所属类。即,任何NSObject继承体系下的元类都使用NSObject的元类作为自己的所属类,而基类的元类的父类是基类,这样就形成了一个闭环。

    实例方法和类方法

    Class的结构体中有一个元素methodLists,当我们调用一个方法时,系统会在这个列表中进行查找,同样元类也有一个methodLists,所以:

    • 当给对象发送消息时,系统会在当前对象对应的类对象的methodLists中进行查找。
    • 当给类发送消息时,系统会在当前类的元类的methodLists中进行查找。

    也就是说,类对象存储着一个类的所有实例方法,元类存储着一个类的所有的类方法。同时,每个类都会有一个单独的元类,因为每个类的类方法基本上不可能完全相同。

    SEL

    sel是系统在编译过程中,会根据方法的名字以及参数序列生成一个用来区分这个方法的唯一ID编号。

    Method

    Method从字面上看就是方法的意思,但它其实就是objc_method的结构体指针。

    IMP

    IMPImplementation,为指向函数实现的指针,如果我们能够获取到这个指针,就可以直接调用该方法。
    获取IMP的方法:

    //通过Method获取IMP
    IMP method_getImplementation(Method m);
    // 返回方法的具体实现
    IMP class_getMethodImplementation ( Class cls, SEL name );
    IMP class_getMethodImplementation_stret ( Class cls, SEL name );
    

    获取到IMP之后可直接调用方法

    SEL aSel = @selector(didReceiveMemoryWarning);
    Method method = class_getInstanceMethod([self class], aSel);
    IMP imp = method_getImplementation(method);
    ((void (*) (id, SEL)) (void *)imp)(self, aSel);
    

    Ivar

    Class结构体中,有一个ivars的链表结构,其存储着所有变量信息(Ivar的数组),每一个Ivar指针对应一个变量元素。同时通过系统的API,我们看到Ivar也是一个结构体,typedef struct objc_ivar *Ivar,它也是一个结构体:

    struct objc_ivar {
        char *ivar_name                                          OBJC2_UNAVAILABLE;
        char *ivar_type                                          OBJC2_UNAVAILABLE;
        int ivar_offset                                          OBJC2_UNAVAILABLE;
    #ifdef __LP64__
        int space                                                OBJC2_UNAVAILABLE;
    #endif
    }
    对`Ivar`的操作有以下方法:
    

    // 获取类中指定名称成员变量
    Ivar class_getInstanceVariable ( Class cls, const char *name );
    // 获取类变量
    Ivar class_getClassVariable ( Class cls, const char *name );
    // 获取整个成员变量列表
    Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );

    - 需要注意的是:`class_copyIvarList`这个函数返回全部实例变量的数组,数组中每个`Ivar`指向该成员变量信息的`objc_ivar`结构体的指针。这个数组不包含在父类中声明的变量。`outCount`指针返回数组的大小,我们必须使用`free()`来释放这个数组。
    
    

    相关文章

      网友评论

          本文标题:Runtime-开篇

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