05-Class的结构以及class_rw_t和class_ro_t的区别

简介: 05-Class的结构以及class_rw_t和class_ro_t的区别

isa地址指向验证

全文围绕下面这样一段代码展开分析:

@protocol PersonProtocal <NSObject>
@optional
- (void)protocalInstanceMethod;
+ (void)protocalClassMethod;
@end
@interface Person : NSObject<PersonProtocal> {
    int _age;
}
@property(assign, nonatomic) int height;
- (void)instanceMethod;
+ (void)classMethod;
@end
@implementation Person
- (void)instanceMethod {}
+ (void)classMethod {}
@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [Person new];
        Class personCls = [person class];
        Class personMetaClass = object_getClass(personCls);
        NSLog(@"%p - %p - %p", person, personCls, personMetaClass);
    }
    return 0;
}

上文(03-isa指针 & superclass指针)我们分析到实例对象的isa指向类对象,接下来我们调试一下:

(lldb) p/x (long)person->isa
(long) $0 = 0x011d800100008579
(lldb) p/x personCls
(Class) $1 = 0x0000000100008578 Person
(lldb)

我们会发现person的isa地址和类对象的地址并不相同,具体是什么原因产生的?我们直接分析objc源码,我们会看到下面这样一段获取真实的isa,真实的isa要对象的地址需要&上ISA_MASK的值,才是真正的isa的值;

ISA_MASK的值:

# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
#endif

再来分析一下isa的值,会发现结论和我们前文所分析的一致,直接看图:

objc_class分析

objc_class结构

首先看一下Class的类型,为objc_class结构体指针;

typedef struct objc_class *Class;

通过objc源码再来看一下objc_class结构体,发现objc_class继承自objc_object:

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache; // 方法缓存
    class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
    class_rw_t *data() const {
        class_rw_t *_data = bits.data(); //(bits & FAST_DATA_MASK)
        return _data;
    }
   ...
};

再看一下objc_object,里面只有一个isa成员(其他成员已废弃、这里没有贴出来):

struct objc_class {
    Class _Nonnull isa OBJC_ISA_AVAILABILITY;
} OBJC2_UNAVAILABLE;
  • superclass:Class类型,代表指向父类的指针,占用8个字节,前文已经分析过它的作用;

  • cache_t:通过以下代码分析,我们可以知道cache_t一共占用了16个字节;

#if __LP64__
typedef uint32_t mask_t; // x86_64 & arm64 asm are less efficient with 16-bits
#else
typedef uint16_t mask_t;
#endif
struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask; //8个字节
    union {
        struct {
            explicit_atomic<mask_t> _maybeMask; //uint16_t 2个字节 uint32_t 4个字节
#if __LP64__
            uint16_t                   _flags; // 2个字节
#endif
            uint16_t                   _occupied; // 2个字节
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache; //8个字节
    };
    ...
}
  • bits:我们可以看到通过调用bits的data函数,我们可以得到一个class_rw_t类型的一个结构体指针,并且bits的值是通过平移上述8(isa指针)+8+16=32个字节得到;

class_rw_t

如下摘取了里面存储的一些主要信息:class_ro_t、methods(方法列表)、properties(属性列表)、protocols(协议列表);其中class_rw_t中还会存储分类里面的内容;

struct class_rw_t {
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
#endif
    explicit_atomic<uintptr_t> ro_or_rw_ext;
    Class firstSubclass;
    Class nextSiblingClass;
    const class_ro_t *ro();
    const method_array_t methods(); //方法列表
    const property_array_t properties(); //属性列表
    const protocol_array_t protocols(); //协议列表
};
• methods:是一个二维数组,里面放着很多个method_list_t(里面放着类本身的方法以及分类的方法),method_list_t中放着很多个method_t,method_t的结构如下:
struct method_t {
    SEL name; //函数名
    const char *types; //编码
    MethodListIMP imp; //using MethodListIMP = IMP; 函数指针
};

SEL:代表方法、函数名,底层结构跟char *类似;可以通过@selector()和sel_registerName()获得;通过sel_getName()和NSStringFromSelector()转成字符串;不同类中相同名字的方法,所对应的方法选择器是相同的;

typedefstructobjc_selector *SEL;

types包含了函数的返回值、参数的编码的字符串,可以利用@encode()返回编码该类型的字符串,具体可以参考Apple官网的映射表:

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.htmlIMP:函数的具体实现:

typedefid _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);

接下来通过lldb调试分析一下实例方法的存储内容,类方法可以直接修改第一步p/x personMetaClass即可:

(lldb) p/x personCls
(Class) $0 = 0x0000000100008658 Person
(lldb) p/x 0x0000000100008658 + 0x20 //此处是因为向右平移了32个字节
(long) $1 = 0x0000000100008678
(lldb) p (class_data_bits_t *)0x0000000100008678
(class_data_bits_t *) $2 = 0x0000000100008678
(lldb) p $2->data()
(class_rw_t *) $3 = 0x0000000101263340
(lldb) p $3->methods()
(const method_array_t) $4 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x00000001000084e0
      }
      arrayAndFlag = 4295001312
    }
  }
}
(lldb) p $4.list
(const method_list_t_authed_ptr<method_list_t>) $5 = {
  ptr = 0x00000001000084e0
}
(lldb) p $5.ptr
(method_list_t *const) $6 = 0x00000001000084e0
(lldb) p *$6
(method_list_t) $7 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 3)
}
(lldb) p $7.get(0).big()
(method_t::big) $8 = {
  name = "instanceMethod"
  types = 0x0000000100003d2e "v16@0:8"
  imp = 0x00000001000039d0 (KCObjcBuild`-[Person instanceMethod] at main.mm:25)
}
(lldb) p $7.get(1).big() //big函数返回method_t里面存储这方法名、类型等信息
(method_t::big) $9 = {
  name = "setHeight:"
  types = 0x0000000100003d40 "v20@0:8i16"
  imp = 0x0000000100003a00 (KCObjcBuild`-[Person setHeight:] at main.mm:19)
}
(lldb) p $7.get(2).big()  
(method_t::big) $10 = {
  name = "height"
  types = 0x0000000100003d38 "i16@0:8"
  imp = 0x00000001000039e0 (KCObjcBuild`-[Person height] at main.mm:19)
}
(lldb) p $7.get(3).big()
Assertion failed: (i < count), function get, file , line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)
  • protocols:存储着协议相关信息;
  • properties:可以看到里面存储着类的属性信息,但并不包括成员变量信息;调试信息如下:

(lldb) p/x personCls
(Class) $0 = 0x0000000100008660 Person
(lldb) p/x 0x0000000100008660+0x20
(long) $1 = 0x0000000100008680
(lldb) p (class_data_bits_t *)0x0000000100008680
(class_data_bits_t *) $2 = 0x0000000100008680
(lldb) p $2->data()
(class_rw_t *) $3 = 0x000000010144d230
(lldb) p *$3
(class_rw_t) $4 = {
  flags = 2148007936
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295001240
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
}
(lldb) p $4.properties()
(const property_array_t) $5 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008578
      }
      arrayAndFlag = 4295001464
    }
  }
}
(lldb) p $5.list
(const RawPtr<property_list_t>) $6 = {
  ptr = 0x0000000100008578
}
(lldb) p $6.ptr
(property_list_t *const) $7 = 0x0000000100008578
(lldb) p *$7
(property_list_t) $8 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 5)
}
(lldb) p $8.get(0)
(property_t) $9 = (name = "height", attributes = "Ti,N,V_height")
(lldb) p $8.get(1)
(property_t) $10 = (name = "hash", attributes = "TQ,R")
(lldb) p $8.get(2)
(property_t) $11 = (name = "superclass", attributes = "T#,R")
(lldb) p $8.get(3)
(property_t) $12 = (name = "description", attributes = "T@\"NSString\",R,C")
(lldb) p $8.get(4)
(property_t) $13 = (name = "debugDescription", attributes = "T@\"NSString\",R,C")
(lldb) p $8.get(5)
Assertion failed: (i < count), function get, file .
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)

class_ro_t

class_ro_t为class_rw_t内的一个成员,存储着编译期已经确定的协议、属性、方法、成员变量,不包含分类的信息;

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif
    explicit_atomic<const char *> name;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars; //成员变量列表
    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
    const char *getName() ;
    method_list_t *baseMethods();
};

主要看一下成员变量的存储:

(lldb) p/x personCls
(Class) $0 = 0x0000000100008660 Person
(lldb) p/x 0x0000000100008660+0x20
(long) $1 = 0x0000000100008680
(lldb) p (class_data_bits_t *)0x0000000100008680
(class_data_bits_t *) $2 = 0x0000000100008680
(lldb) p $2->data()
(class_rw_t *) $3 = 0x000000010152a830
(lldb) p *$3
(class_rw_t) $4 = {
  flags = 2148007936
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295001104
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
}
(lldb) p $4.ro()
(const class_ro_t *) $6 = 0x0000000100008410
(lldb) p *$6
(const class_ro_t) $7 = {
  flags = 128
  instanceStart = 8
  instanceSize = 16
  reserved = 0
   = {
    ivarLayout = 0x0000000000000000
    nonMetaclass = nil
  }
  name = {
    std::__1::atomic<const char *> = "Person" {
      Value = 0x0000000100003c74 "Person"
    }
  }
  baseMethodList = 0x0000000100008458
  baseProtocols = 0x00000001000083f8
  ivars = 0x00000001000084a8
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x00000001000084f0
  _swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $7.ivars
(const ivar_list_t *const) $8 = 0x00000001000084a8
(lldb) p *$8
(const ivar_list_t) $9 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 2)
}
(lldb) p $9.get(0)
(ivar_t) $10 = {
  offset = 0x0000000100008628
  name = 0x0000000100003f22 "_age"
  type = 0x0000000100003d3a "i"
  alignment_raw = 2
  size = 4
}
(lldb) p $9.get(1)
(ivar_t) $11 = {
  offset = 0x0000000100008630
  name = 0x0000000100003f27 "_height"
  type = 0x0000000100003d3a "i"
  alignment_raw = 2
  size = 4
}
(lldb) p $9.get(2)
Assertion failed: (i < count), function get, file , line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)

总结

Class与objc_class以及objc_object的关系?

Class为为objc_class结构体指针,objc_class继承自objc_object,objc_object里面只有一个isa成员;

class_rw_t和class_ro_t的区别?

class_ro_t为class_rw_t内的一个成员,存储着编译期已经确定的类里面的协议、属性、方法、成员变量信息,不包含分类的信息;

class_rw_t里面主要存储:class_ro_t、methods(方法列表)、properties(属性列表)、protocols(协议列表)、以及分类里面的内容,不包含成员变量信息;

运行时会将class_ro_t里面内容合并到class_rw_t中(具体是在realizeClass函数中,可参考源码),之后又将分类里面的内容也合并到class_rw_t中;本文部分内容来可能来源于网络,发布的内容如果侵犯了您的权益,请联系我们尽快删除!

相关文章
|
前端开发
class id
class id
71 0
|
4月前
|
存储 JavaScript 前端开发
ES6 class 类
【7月更文挑战第27天】
21 1
|
编译器 C语言 C++
C++中的 class和struct区别
C++ 中保留了C语言的 struct 关键字,并且加以扩充。在C语言中,struct 只能包含成员变量,不能包含成员函数。而在C++中,struct 类似于 class,既可以包含成员变量,又可以包含成员函数。 C++中的 struct 和 class 基本是通用的,唯有几个细节不同: 使用 class 时,类中的成员默认都是 private 属性的;而使用 struct 时,结构体中的成员默认都是 public 属性的。 class 继承默认是 private 继承,而 struct 继承默认是 public 继承(《C++继承与派生》一章会讲解继承)。 class 可以使用模板,
135 0
|
Java Spring
FileSystemResource和ClassPathResource有何区别?
FileSystemResource和ClassPathResource有何区别?
FileSystemResource和ClassPathResource有何区别?
|
JavaScript 前端开发 程序员
Class-总结 class 的基本用法和两个注意点|学习笔记
快速学习 Class-总结 class 的基本用法和两个注意点
185 0
|
存储 缓存 C语言
iOS - isa、class-rw-t、class-ro-t结构体
iOS - isa、class-rw-t、class-ro-t结构体
iOS - isa、class-rw-t、class-ro-t结构体
|
JavaScript 程序员
Class-总结class的基本用法和两个注意点
一、注意点一:class关键字区间使用 二、注意点二:Class关键字内部函数
4.16.2假设有如下程序: public class Demo
16.假设有如下程序: public class Demo { public static void main(String args[]) { int num = 50 ; num = num ++ * 2 ; System.out.println(num) ; } } 最终的执行结果是什么? A. 50 B. 102 C. 100 D. 101 相关知识点: https://edu.aliyun.com/course/34 正确答案:C
1541 0
|
数据安全/隐私保护 C++
论class和struct的区别
论class和struct的区别
138 0