OC面试相关

iOS class_rw_t 和 class_ro_t 及cla

2020-09-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
/// 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

上一篇下一篇

猜你喜欢

热点阅读