ro 和 rw的区别
从生成时机的角度来说,
ro
编译阶段生成,rw
运行的时候生成。从存储的内容角度来讲,ro
中有方法、属性、协议和成员变量,而rw
中并没有成员变量。rw
中的方法属性协议的取值方法中,也是通过取ro
或者rwe
中的值来获得。ro
中的方法、属性、协议都是base,也就是只有本类中的方法属性和协议。
class_rw_ext_t
在2020年的wwdc中有个视频,对ro
和rw
进行了解释。
其主要内容已截图:
代码:
$ heap Mail | egrep 'class_rw|COUNT'
我们再打开mail APP随便打开几个页面使用下,再看看ro
的情况。
结合视频和自己实验的数据可以总结一下:
rw_ext_t
生成条件
- 使用分类的类。
- 使用Runtime API动态修改类的结构的时候。
在遇到以上2种情况的时候,类的结构(属性、协议、方法)发生改变,原有的
ro
(Claer Memory,便宜)已经不能继续记录类的属性、协议、方法信息了,于是系统重新生成可读可写的内存结构rw_ext
(Dirty Memory, 比较贵),来存放新的类结构。
然后再取方法、属性、列表的时候,在方法实现中来做区分,如果有rw_ext的类,其列表就错那个rw_ext中获得,如果没有,从ro中读取。
这样做的目的是为了在不增加成本的情况下,提高性能。使用分类和使用Runtime改变类结构的情况一般占10%左右,这10%的类结构苹果使用比较贵的内存记录(可读可写),而没有变过的类结果,我们用便宜的内存记录(只读)。这样既保证了语言的动态性,又不会因为动态性而增加过多的成本。
类方法的存储
类方法是存放在元类里面的。
思考
元类里面存放的内容只有类方法,那么为什么不把类方法存放在类对象中?或者说设计元类的目的是什么呢?
-
单一职责设计原理。 实例对象存储成员变量的值,类对象存放,实例方法、协议、成员变量、属性,元类对象存放类方法,各司其职,互不影响。
-
复用
msgSend
消息发送机制。
类方法、实例方法是在上层的定义,在底层并不区分类方法实例方法,但在runtime这一层,需要承接上层类方法和实例方法,对接到底层方法调用。使用了msgSend
,如果msgSend
的时候需要再区分类对象,实例对象,会在内部增加判读逻辑,从而降低了效率,有了元类的存在,问题迎刃而解。