美文网首页OC面试相关
iOS class_rw_t 和 class_ro_t 及cla

iOS class_rw_t 和 class_ro_t 及cla

作者: 水煮杰尼龟 | 来源:发表于2020-09-30 17:30 被阅读0次

       先来看看 class_rw_tclass_ro_t 的结构

    struct class_rw_t {
        // Be warned that Symbolication knows the layout of this structure.
        uint32_t flags;
        uint16_t version;
        uint16_t witness;
    
        const class_ro_t *ro;
    
        method_array_t methods;
        property_array_t properties;
        protocol_array_t protocols;
    
        Class firstSubclass;
        Class nextSiblingClass;
    
        char *demangledName;
    
    #if SUPPORT_INDEXED_ISA
        uint32_t index;
    #endif
    };
    
    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;
    };
    

    可以了解到class_rw_tclass_ro_t中都有方法,属性,协议这些东西,而且class_rw_t中还有一个只读的class_ro_tclass_ro_t中多了ivars成员变量等

    class_rw_t我们在类结构中见过,通过bits.data()可以得到

    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
    
        class_rw_t *data() const {
            return bits.data();
        }
    }
    

    在前面一篇文章iOS 捋一捋Category加载流程及+load中,我们知道运行时会对类,分类等做一些处理。在runtime的入口函数_objc_init中,往后走会调用realizeClassWithoutSwift这个方法。
    掏出一部分源码过来分析,我们在这里打一个Person类的断点

    ro = (const class_ro_t *)cls->data();
        if (strcmp(tempChar, "Person")==0) {
            
        }
        if (ro->flags & RO_FUTURE) {
            // This was a future class. rw data is already allocated.
            rw = cls->data();
            ro = cls->data()->ro;
            cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
        } else {
            // Normal class. Allocate writeable class data.
            rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
            rw->ro = ro;
            rw->flags = RW_REALIZED|RW_REALIZING;
            cls->setData(rw);
        }
    

    这里你会发现cls->data()是用ro接收的,按我们看的源码结构不是应该是rw吗。
    我们打印看看ro有些什么

    Person
    (lldb) p *ro
    (const class_ro_t) $0 = {
      flags = 128
      instanceStart = 8
      instanceSize = 9
      reserved = 0
      ivarLayout = 0x0000000000000000
      name = 0x0000000100003eeb "Person"
      baseMethodList = 0x0000000100008350
      baseProtocols = 0x0000000000000000
      ivars = 0x00000001000083a0
      weakIvarLayout = 0x0000000000000000
      baseProperties = 0x00000001000083c8
      _swiftMetadataInitializer_NEVER_USE = {}
    }
    (lldb) p $0.ivars
    (const ivar_list_t *const) $1 = 0x00000001000083a0
    (lldb) p *$1
    (const ivar_list_t) $2 = {
      entsize_list_tt<ivar_t, ivar_list_t, 0> = {
        entsizeAndFlags = 32
        count = 1
        first = {
          offset = 0x0000000100008438
          name = 0x0000000100003f60 "_sex"
          type = 0x0000000100003faa "c"
          alignment_raw = 0
          size = 1
        }
      }
    }
    (lldb) p $0.baseMethodList
    (method_list_t *const) $3 = 0x0000000100008350
    (lldb) p *$3
    (method_list_t) $4 = {
      entsize_list_tt<method_t, method_list_t, 3> = {
        entsizeAndFlags = 24
        count = 3
        first = {
          name = "sayYes"
          types = 0x0000000100003f65 "v16@0:8"
          imp = 0x0000000100003c80 (KCObjcTest`-[Person sayYes] at Person.m:14)
        }
      }
    }
    (lldb) p $4.get(1)
    (method_t) $5 = {
      name = "sex"
      types = 0x0000000100003f97 "c16@0:8"
      imp = 0x0000000100003cc0 (KCObjcTest`-[Person sex] at Person.h:14)
    }
    (lldb) p $4.get(2)
    (method_t) $6 = {
      name = "setSex:"
      types = 0x0000000100003f9f "v20@0:8c16"
      imp = 0x0000000100003ce0 (KCObjcTest`-[Person setSex:] at Person.h:14)
    }
    (lldb) p $0.baseProperties
    (property_list_t *const) $7 = 0x00000001000083c8
    (lldb) p *$7
    (property_list_t) $8 = {
      entsize_list_tt<property_t, property_list_t, 0> = {
        entsizeAndFlags = 16
        count = 1
        first = (name = "sex", attributes = "Tc,N,V_sex")
      }
    }
    

    发现成员变量,属性,方法都已经在里面了。
    那我们可以知道class_ro_t存储了当前类在编译期就已经确定的属性、方法以及遵循的协议等, 而到了运行时这里做了class_rw_t的处理,这里会生成class_rw_t结构体,将class_rw_tro赋予class_ro_t,并且更新data部分,换成class_rw_t

     rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
            rw->ro = ro;
            rw->flags = RW_REALIZED|RW_REALIZING;
            cls->setData(rw);
    

    而后会有分类的处理,分类的信息处理则都是放在rw中的。

    总结一下class_rw_t 和 class_ro_t

    class_ro_t存储了类在编译期已经确定的属性、方法以及遵循的协议,类结构中bits一开始存储的是class_ro_t,运行时会创建class_rw_t ,并且把class_ro_t赋值给class_rw_tro,然后刷新bits, class_ro_t是只读的,运行时我们给class添加方法等操作,都是通过class_rw_t来实现的。

    接着我们看看class_copyIvarList & class_copyPropertyList

    当我们@property(nonatomic,copy)NSString *name;声明一个属性的时候,我们都知道编译器会帮我们创建实例变量_name,并且声明且实现name属性的setter、getter方法。
    @property = ivar + getter + setter;
    再看一下这俩方法的源码

    Ivar *
    class_copyIvarList(Class cls, unsigned int *outCount)
    {
        const ivar_list_t *ivars;
        Ivar *result = nil;
        unsigned int count = 0;
    
        if (!cls) {
            if (outCount) *outCount = 0;
            return nil;
        }
    
        mutex_locker_t lock(runtimeLock);
    
        ASSERT(cls->isRealized());
        
        if ((ivars = cls->data()->ro->ivars)  &&  ivars->count) {
            result = (Ivar *)malloc((ivars->count+1) * sizeof(Ivar));
            
            for (auto& ivar : *ivars) {
                if (!ivar.offset) continue;  // anonymous bitfield
                result[count++] = &ivar;
            }
            result[count] = nil;
        }
        
        if (outCount) *outCount = count;
        return result;
    }
    
    objc_property_t *
    class_copyPropertyList(Class cls, unsigned int *outCount)
    {
        if (!cls) {
            if (outCount) *outCount = 0;
            return nil;
        }
    
        mutex_locker_t lock(runtimeLock);
    
        checkIsKnownClass(cls);
        ASSERT(cls->isRealized());
        
        auto rw = cls->data();
    
        property_t **result = nil;
        unsigned int count = rw->properties.count();
        if (count > 0) {
            result = (property_t **)malloc((count + 1) * sizeof(property_t *));
    
            count = 0;
            for (auto& prop : rw->properties) {
                result[count++] = &prop;
            }
            result[count] = nil;
        }
    
        if (outCount) *outCount = count;
        return (objc_property_t *)result;
    }
    

    发现一个是从roivars里取, 一个是从rwproperties里取。
    我们正常使用property也会生成对应的实例变量,所以 roivars 也可以查到,

    so
    • class_copyIvarList获取类中的所有实例变量信息,从class_ro_t的ivars中获取
    • class_copyPropertyList 获取类中的属性信息,从 class_rw_tproperties中获取
      验证一下
    /// person里增加
    {
        NSString *_nickName;
    }
    
    @property(nonatomic,assign)BOOL sex;
    
    /// 打印一下
    unsigned int ivarsCount = 0;
            Ivar *ivars = class_copyIvarList([Person class], &ivarsCount);
            for (int i = 0; i<(int)ivarsCount; i++) {
                Ivar ivar = ivars[i];
                const char *name = ivar_getName(ivar);
                NSString *s = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
                NSLog(@"class_copyIvarList -- %@",s);
            }
            NSLog(@"----------------------------------------");
            unsigned int propertiesCount = 0;
            objc_property_t *properties = class_copyPropertyList([Person class], &propertiesCount);
            for (int i = 0; i<(int)propertiesCount; i++) {
                objc_property_t property = properties[i];
                const char *name = property_getName(property);
                NSString *s = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
                NSLog(@"class_copyPropertyList -- %@",s);
            }
    
    结果如下

    [20877:1150546] class_copyIvarList -- _nickName
    [20877:1150546] class_copyIvarList -- _sex
    [20877:1150546] ----------------------------------------
    [20877:1150546] class_copyPropertyList -- sex

    打印结果验证了理论。

    end

    相关文章

      网友评论

        本文标题:iOS class_rw_t 和 class_ro_t 及cla

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