美文网首页
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