Objective-C基础(三)

1. objc_class结构体

我们在OC中所写的每一个类,其底层其实都是利用struct结构体来实现的。其中objc_class就是用来保存类和元类对象的结构体。

学习这个结构体对于理解我们上篇文章中提到的OC对象以及之后要讲的Runtime等内容都有很大帮助。

结构体的定义如下(注:为了方便理解和记忆,只保留结构体中重要字段,其余字段省略):

struct objc_class {
    Class isa; // isa指针
    Class superclass; // superclass指针
    cache_t cache; // 方法缓存
    class_data_bits_t bits; // 指向具体的类信息的指针
}

其中,isa和superclass指针见上篇文章; cache是一个方法缓存,可以理解为一张哈希表,存储着这个类最近被调用的方法; bits是一个指针,指向一个class_rw_t结构体,其中包含具体的类信息。 class_rw_t结构体的定义如下:

struct class_rw_t {
    const class_ro_t *ro; // 指向只读信息
    
    struct method_list_t **methods; // 方法列表
    struct chained_property_list *properties; // 属性列表
    struct protocol_list_t ** protocols; // 协议列表
}

首先,class_rw_t中的rw意为readwrite,即这个结构体是可读写的,这也表明类的方法列表,属性列表,协议列表,是可读写的,这在之后的Runtime中会有相应的体现,大家可以先记住。 其次,class_ro_t中的ro意为readonly,即这个结构体指针指向的结构体是只读的(被const修饰),class_ro_t的定义如下:

struct class_ro_t {
    uint32_t instanceSize; // instance对象占用的内存
    const char * name; // 类名
    const ivar_list_t * ivars; // 成员变量列表
}

首先,上一篇文章中我们讲到,instance对象中,仅保存成员变量的值,因此,如果成员变量列表ivars是不变的,那么instance对象占用的内存instanceSize自然是不变的,因此他们是只读的。 其次,一个类的类名始终不变。

注意:objc_class用来保存类和元类对象,也就是说class对象和meta-class对象的内存结构是一样的。我们在上篇文章中提到过,class对象存储着属性信息、方法信息、协议信息和成员变量信息;而meta-class对象只存储类方法信息,因此meta-class的属性信息、协议信息、成员变量信息这些字段虽然存在,但是值为空。

关于源代码,请参考Apple官方:https://opensource.apple.com/source/objc4/objc4-437/runtime/objc-runtime-new.h.auto.html

2. getInstanceSize / malloc_size / sizeof

  1. getInstanceSize计算的是一个OC对象 至少需要的内存,存在结构体内存对齐,即结构体大小必须是最大成员大小的倍数。
  2. malloc_size计算的是一个OC对象 实际占用的内存,存在系统内存对齐,即对象占用内存大小必须是系统规定的单位的倍数(栈内存8字节、堆内存16字节)。

举个例子,假设一个类不存在任何成员变量,那么在创建这个类的一个instance对象,instance对象中就只存储着一个指向类对象的isa指针,在64位操作系统下,这个指针占8字节。 那么,如果对这个对象调用getInstanceSize方法,返回的结果就是8字节,即这个对象至少需要的内存。 如果对这个对象调用malloc_size方法,由于对象存储在堆上,因此需要做系统内存对齐(扩展为16的倍数),返回的结果为16字节,即这个对象实际占用的内存。 也就是说,在创建对象的时候,如果对象需要的空间小雨16字节,系统就会硬性分配16字节的空间给对象。

关于结构体内存对齐和系统内存对齐,面试官基本不咋考,感兴趣的同学可以上网搜索资料自行学习。

最后,关于sizeof,它与上面提到的两个函数之间的区别在于:

  • sizeof是运算符,不是函数
  • 只能计算基本数据类型的占用空间,不能传入一个OC对象
  • sizeof是运算符,其返回值在编译期就已经决定了,例如如果传入的是一个bool类型的变量,就返回1,int返回4。因此,它的运行效率比上面提到的两个函数要高

好啦,今天就先讲到这里。

我的牛客网账号是917470656,上面有我记录的几篇面经。

个人公众号:iOS开发学习

未经作者允许,禁止转载!

#iOS开发工程师##iOS开发工程师实习生##iOS工程师##iOS开发实习生##学习秋招#
iOS开发学习 文章被收录于专栏

学习iOS开发 == 手握大厂offer

全部评论

相关推荐

牛客101244697号:这个衣服和发型不去投偶像练习生?
点赞 评论 收藏
分享
joe2333:怀念以前大家拿华为当保底的日子
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务