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中;本文部分内容来可能来源于网络,发布的内容如果侵犯了您的权益,请联系我们尽快删除!

相关文章
|
JSON 自然语言处理 编译器
Alibaba.com瘦包40MB——业界最全的iOS包大小技术总结
前言包大小是衡量APP性能的一项重要指标,它直接影响用户的下载点击率(包太大不想下)、下载安装成功率(下载慢不用了)、APP卸载率(太占空间先删掉)。包大小的计算逻辑很简单,它是各种类型的文件占用磁盘大小相加。APP瘦身的技术却很复杂,代码文件的复杂度和编译器策略决定了可执行文件的大小,业务功能和工程架构决定了代码文件的复杂度。iOS APP瘦身,需要掌握的技能有XCode构建技术、LLVM编译器
4602 0
Alibaba.com瘦包40MB——业界最全的iOS包大小技术总结
|
存储 缓存 关系型数据库
sqlite 数据库 介绍
sqlite 数据库 介绍
379 0
|
Swift 索引
Swift开发——元组
Swift中的元组是一种数据结构,用于组合不同类型的值。它们不是独立的数据类型,而是以有序序列形式存在,用圆括号括起,元素间用逗号分隔。元组可以有任意数量和类型的元素,可变性取决于其定义。常用于函数返回多个值。示例代码展示了元组的创建、访问、解包及赋值。元组可以通过标签来标识元素,支持嵌套和比较。在函数返回值和并行赋值场景中,元组特别有用。
303 0
Swift开发——元组
|
机器学习/深度学习 人工智能 自然语言处理
|
存储 NoSQL 算法
分布式唯一 ID 的 7 种生成方案
在互联网的业务系统中,涉及到各种各样的ID,如在支付系统中就会有支付ID、退款ID等。那一般生成ID都有哪些解决方案呢?特别是在复杂的分布式系统业务场景中,我们应该采用哪种适合自己的解决方案是十分重要的。下面我们一一来列举一下,不一定全部适合,这些解决方案仅供你参考,或许对你有用。
分布式唯一 ID 的 7 种生成方案
|
存储 算法 安全
Hash 算法详细介绍与实现 (二)
书接上回,昨天写了第一部分,《Hash 算法详细介绍与实现 (一)》详细介绍了Hash表和Hash算法的相关概念以及算法的基本原理。同时简单介绍几种hash算法的实现:直接取余法和乘法取整法;本文接着详细唠唠Hash算法和Hash表这个数据结构的具体实现以及Hash算法和Hash表常见问题的解决方案,比如解决Hash表的冲突问题等等.相关的理论知识已在上篇文章详细介绍,这里就不再赘述,多的不说少的不唠,直接进入今天的主题:利用Hash算法实现Hash表
692 1
|
安全 算法 网络安全
HTTPS 的加密流程
HTTPS (Hyper Text Transfer Protocol Secure) 是基于 HTTP 协议之上的安全协议,用于在客户端和服务器之间通过互联网传输数据的加密和身份验证。它使用 SSL/TLS (Secure Sockets Layer/Transport Layer Security) 协议来保护数据的安全性,可以防止数据被窃听、篡改或伪造。
827 3
|
存储 数据安全/隐私保护
App逆向百例|16|某交友App花指令分析
App逆向百例|16|某交友App花指令分析
709 0
|
存储 编译器 C++
【C++】深度剖析string类的底层结构及其模拟实现(一)
【C++】深度剖析string类的底层结构及其模拟实现
393 0
|
机器学习/深度学习 iOS开发 计算机视觉
iOS MachineLearning 系列(16)—— 几个常用的图片分类CoreML模型
对于图片识别分类的模型来说,其输入和输出都一样,输入都为图像参数,输入为两部分,一部分为最佳预测结果,一部分为可能得预测结果及其可信度。
683 0