WWDC2020介绍Objective-C 运行时的改进,主要介绍了三个变化:
1.Class data structures changes;
首先是数据结构的变化,Objective-C 运行时会使用他们来追踪类。
2.Relative method lists;
Objective-C方法列表的变化
3.tagged pointer 格式的变化;
本文先对Class data structures changes展开讲解:
Superclass.png
在磁盘上,在你的app二进制文件中 类时这样的:
首先这个类对象本身,包含了最常被访问的信息指向
元类(Metaclass)、超类(Superclass)和方法缓存的指针(Method cache)。
它还有一个指向更多数据的指针,存储额外信息的地方叫做class_ro_t, ro代表只读,它包括像类名称,方法、协议和实例变量的信息。 class_ro_t.png
Swift 类和Objective-C类共享这一基础结构,所以每个Swift类也有这些数据结构,当类第一次从磁盘加载到内存中时,他们一开始也是这样的,但是一经使用,他们就会发生变化。
这里先区分下:clean memory 和dirty memory
clean memory:是指加载后不会发生更改的内存。class_ro_t 就属于clean memory因为它是属于只读的。
dirty memory:是指在进程运行时会发生更改的内存。
类结构一经使用就会变成dirty memory,因为运行时会向它写入新的数据。
例如:创建一个新的方法缓存并从类中指向它,dirty memory 比clean memory要昂贵得多,只要进程在运行,它就一直存在。
另一方面clean memory 可以进行移除从而节省更多的内存空间,因为如果你需要clean memory系统可以从磁盘中重新加载,MacOS 可以选择换出dirty memory,但是因为iOS不使用swap,所以dirty memory在iOS中代价很大,dirty memory是这个类数据被分成两部分的原因,可以保持清洁的数据越多越好,通过分离那些永远不会更改的数据,可以把大部分的类数据存储为clean memory。
这些数据足以让我们开始,但是运行时需要追踪每个类的更多信息,所以当一个类首次被使用,运行时会为它分配额外的存储容量,这个运行时分配的存储容量是class_rw_t 用于读取-编写数据,在这个数据结构中,我们存储了只有在运行时才会生成的新信息。
class_rw_t.png
例如:所有的类都会链接成一个树状结构,这是通过使用First Subclass 和 Next sibling Class 指针实现的,这运行时遍历当前使用的所有类,对于使方法缓存无效非常有用。
class_rw_t.png
但为什么方法和属性也在只读数据中时这里还有方法和属性呢?
class_rw_t.png
因为它们可以在运行时进行更改。
当category被加载时它可以向类中添加新的方法,而且开发者可以使用运行时API动态地添加他们,因为class_ro_t是只读的,所以我们需要在class_rw_t中追踪这些东西。
现在这样做会占用相当多的内存,在任何给定的设备中都有许多类在使用。我们在iPhone上的整个系统中测量了,大约30兆字节这些class_rw_t结构,那么如何缩小这些数据结构呢?
在读取-编写部分需要这些东西,因为他们可以喜爱运行时进行更改,但是通过检查实际设备上的使用情况,我们发现大约只有10%的类真正地更改了他们的方法,只有Swift类会使用这个demangled name字段,但是Swift类并不需要这一字段,除非有东西询问它们的 Objective-C名称时才需要 class_rw_t.png
所以,我们可以拆掉那些平时不用的部分,这将class_rw_t的大小减少了一半 class_rw_t.png
对于确实需要额外信息的类,我们可以分配这些扩展记录中的一个,并把它滑到类中供其使用。
class_rw_t.png
大约90%的类从不需要这些扩展数据,这在系统范围内可以节约大约14MB的内存,这些内存现在可以用于更有效的用途,比如存储你的APP的数据,因此 实际上你可以在你的Mac上看到这一变化带来的影响,这只需要在终端机上运行一些简单的命令,现在一起来看一下。
heap Mail.png
在此进入Macbook的终端,要运行一个命令它在任何Mac上都可用叫做heap,它还允许你检查正在运行的进程所使用的堆内存,将在Mac中的Mail app上运行它 ,现在 如果我运行该命令,他会输出千行,显示通过邮件进行的每个堆分配,所以 我只是要 grep 它为 我们今天一直在谈论的类型 class_rw_t类型 我还需要查询标头 从返回的结果中,
可以看到 在邮件app中使用了大约9000个这样的class_rw_t类型,但是其中大约十分之一 900 多一点实际上需要使用这一扩展信息,所以可以很容易地计算出通过这个改变所有节省的内存,这是大小减半的类型,所以如果我们从这个数字中减去,必须分配给扩展类型的内存量,可以看到节省了大约1兆字节数据的四分之一。
如果在系统范围内进行扩展,对dirty memory而言 这是真正的节省内存。
现在很多从类获取数据的代码,必须同时处理那些有扩展数据和没有扩展数据的类,运行时会为你处理这一切,并且从外部看 一切都像往常一样工作,只是使用了更少内存,之所以会这样,是因为读取这些结构的代码,都在运行时内并且还会同时进行更新. class_rw_t.png
坚持使用这些API真的很重要
class_rw_t.png
因为试图直接访问这些数据的结构的代码,都将在今年的OS版本中停止工作,旧东西已经发生了变化,而且该代码不知道新的布局,看到一些真实的代码由于这些变化而崩溃,除了你自己的代码之外,还要注意哪些外部依赖性,可能正把他们带入到你的APP中,他们可能会在你没有意识的情况下挖掘这些数据结构,
这些结构中的所有信息都可以通过官方API获取,有一些函数 如class_getName和class_getSuperclass当你使用这些API访问信息时,你知道 无论我们在后台进行什么更改,他们都将继续工作,所有的API都可以在Objective-C运行时说明文档中找到。
以上就是WWDC2020介绍Objective-C 运行时的改进之数据结构的变化的一些记录,希望对大家的学习有所帮助。
网友评论