美文网首页
2019-12-30

2019-12-30

作者: otc1 | 来源:发表于2020-05-06 17:42 被阅读0次

方法的存储以及方法缓存的存储

类和结构体

首先定义一个类testClass,用clang重写一下,为了方便,不要加其他系统头文件,不然还得指定路径
类被编译成了结构体
struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
     。。。。
}
typedef struct objc_class *Class;

struct NSObject_IMPL {
    Class isa;
};
struct testClass_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int a;
    NSString *b;
};

类中的成员对象变成结构体的成员,方法是否声明不影响c++文件,下面还有属性的set和get方法


image.png
image.png
static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[6];
} _OBJC_$_INSTANCE_METHODS_testClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    6,
    {{(struct objc_selector *)"sayABC", "v16@0:8", (void *)_I_testClass_sayABC},
    {(struct objc_selector *)"eatFood", "v16@0:8", (void *)_I_testClass_eatFood},
    {(struct objc_selector *)"c", "i16@0:8", (void *)_I_testClass_c},
    {(struct objc_selector *)"setC:", "v20@0:8i16", (void *)_I_testClass_setC_}, 
/*
        static void _I_testClass_setC_(testClass * self, SEL _cmd, int c) { (*(int *)((char *)self 
        +OBJC_IVAR_$_testClass$_c)) = c; }
        这个“v20@0:8i16”对应上面的_I_testClass_setC_函数  -- v代表void,返回类型。20指总共 
        长度。@代表id类型,起始位置为0。:代表sel,起始位置为8。i代表int,起始位置为16
*/
    {(struct objc_selector *)"d", "@16@0:8", (void *)_I_testClass_d},
    {(struct objc_selector *)"setD:", "v24@0:8@16", (void *)_I_testClass_setD_}}
};
static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[1];
} _OBJC_$_CLASS_METHODS_testClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"startRun", "v16@0:8", (void *)_C_testClass_startRun}}
};

注意,类方法和实例方法没有在一起,类方法在下面那个结构体里面

属性和方法的存储

根据上面类的结构,使用lldb在xcode中打印


image.png

(由于失误把之前的数据清理,将就一下)$$3就是上面的 $6,在method里面找方法
可以看到,第一个还是正常的方法,后面几个就不知道是什么东西了,可能是缓存之类的东西


image.png
image.png
同样,属性也是一样,第一个貌似是正常的
image.png

class_rw_t结构

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;

在class_rw_t里,发现有个class_ro_t,里面有方法和属性list,再次使用lldb查看

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};

先看method_list_t,可以发现是继承entsize_list_tt,这个结构体里面有两方法都可以用,可以拿到指定的方法,但是把所有的方法打印出来以后,并没有发现存在startRun

    Element& getOrEnd(uint32_t i) const { 
        assert(i <= count);
        return *(Element *)((uint8_t *)&first + i*entsize()); 
    }
    Element& get(uint32_t i) const { 
        assert(i < count);
        return getOrEnd(i);
    }
image.png

然后看ivar,有四个,两个属性加两个成员变量


image.png

class_ro_t这个结构体里面还有属性和协议列表,就不一一打印了。

startRun这个类方法找不到,可能在原类里面,懒得算,直接用object_getClass来拿到isa使用lldb再次打印,第一个就是startRun


image.png

最后总结

对象的方法和属性都在类结构体的class_rw_t结构体的class_ro_t中,多个对象公用一个类结构体,注意这里类中存的属性并不是值,而是属性的名字和类型,值还是在对象里面,而方法是公用的,所以就直接存在类中,类方法在原类中。

既然类在编译的时候已经确定(使用clang可以看到cpp文件),那么使用分类添加的方法和使用runtime添加的属性在哪里呢?


image.png

给TPerson加个分类的gogogo方法发现他会在ro中

相关文章

网友评论

      本文标题:2019-12-30

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