iOS八股文(四)类对象的结构(下)

669 阅读2分钟

ro 和 rw的区别

从生成时机的角度来说, ro编译阶段生成,rw运行的时候生成。从存储的内容角度来讲,ro中有方法、属性、协议和成员变量,而rw中并没有成员变量。rw中的方法属性协议的取值方法中,也是通过取ro或者rwe中的值来获得。ro中的方法、属性、协议都是base,也就是只有本类中的方法属性和协议。

class_rw_ext_t

在2020年的wwdc中有个视频,对rorw进行了解释。 其主要内容已截图:

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

代码:

$ heap Mail | egrep 'class_rw|COUNT'

image.png

image.png

我们再打开mail APP随便打开几个页面使用下,再看看ro的情况。

image.png

结合视频和自己实验的数据可以总结一下:

rw_ext_t生成条件

  1. 使用分类的类。
  2. 使用Runtime API动态修改类的结构的时候。

在遇到以上2种情况的时候,类的结构(属性、协议、方法)发生改变,原有的ro(Claer Memory,便宜)已经不能继续记录类的属性、协议、方法信息了,于是系统重新生成可读可写的内存结构rw_ext(Dirty Memory, 比较贵),来存放新的类结构。

然后再取方法、属性、列表的时候,在方法实现中来做区分,如果有rw_ext的类,其列表就错那个rw_ext中获得,如果没有,从ro中读取。

image.png

这样做的目的是为了在不增加成本的情况下,提高性能。使用分类和使用Runtime改变类结构的情况一般占10%左右,这10%的类结构苹果使用比较贵的内存记录(可读可写),而没有变过的类结果,我们用便宜的内存记录(只读)。这样既保证了语言的动态性,又不会因为动态性而增加过多的成本。

类方法的存储

类方法是存放在元类里面的。

思考

元类里面存放的内容只有类方法,那么为什么不把类方法存放在类对象中?或者说设计元类的目的是什么呢?

  1. 单一职责设计原理。 实例对象存储成员变量的值,类对象存放,实例方法、协议、成员变量、属性,元类对象存放类方法,各司其职,互不影响。

  2. 复用msgSend消息发送机制。

类方法、实例方法是在上层的定义,在底层并不区分类方法实例方法,但在runtime这一层,需要承接上层类方法和实例方法,对接到底层方法调用。使用了msgSend,如果msgSend的时候需要再区分类对象,实例对象,会在内部增加判读逻辑,从而降低了效率,有了元类的存在,问题迎刃而解。