热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

类的内存结构优化

WWDC2020对runtime的优化视频的观看地址:https://developer.apple.com/videos/play/wwdc2020/101

WWDC2020对runtime的优化

  • 视频的观看地址:https://developer.apple.com/videos/play/wwdc2020/10163/ (最好用Safari浏览器打开)
  • LLVM源码地址:https://github.com/apple/llvm-project
    看完视频后总结:本次改动不需要改动任何代码,也不用学习新的API,这次主要是runtime关于内存的优化。在这种环境下我们不用改APP也会运行得比之前更快更高效。

类的数据结构

在《类的探究分析》一文中就详细地解读了类的结构。在APP编译成二进制文件中,类的数据结构发生了变化,其中包含了最常被访问的信息,指向元类、父类和方法缓存的指针。以下是类的数据结构图:

类的内存结构优化
类的数据结构

Clean Memory 和 Dirty Memory的区别

Clean Memory

Clean Memory指的是在程序运行中不会发生改变的内存class_ro_t就是属于Clean Memory的,class_ro_t`内存图解:

类的内存结构优化
class_ro_t数据结构
  • clean memory 加载后不会发生改变的内存
  • class_ro_t 就属于clean memory,因为它是只读的,不会对齐内存进行修改
  • clean memory 是可以进行移除的,从而节省更多的内存空间,因为如果你有需要clean memory,系统可以从磁盘中重新加载

Dirty Memory

Dirty Memory指的是在程序运行中会发生改变的内存,也就是我们俗称的脏数据。类的结构一经使用就会变成Dirty Memory,因为运行时会向它写入新的数据。例如往类中添加方法又或者加载类的子类父类,这里指的是class_rw_t。class_rw_t内存图解如下:

类的内存结构优化
class_rw_t数据结构
  • Dirty Memory是这个类被分成两部分的原因,可以保持类加载后不会发生更改的数据越多越好,通过分离永远不会更改的数据,可以把大量的类数据存储为Clean Memory
  • class_rw_t(读写):类在加载的时候,属性(properties)协议(prococols)方法(methods)会被运行时动态的添加,也可以动态的修改(Method Swizzling)。所以类需要保存在class_rw_t中。
  • First Subclass、Next Subling Class:包含了运行时才会生成的信息First Subclass、Next Subling Class,所有的类都会变成一个树状结构,就是通过First SubclassNext Subling Class指针实现的,它允许运行时遍历当前使用的所有类
  • Demangled Name:这个字段使用的频率是比较少的,swift中才会使用。

总结:

dirty memory要比clean memory更有价值而且要多,只要进行运行它就必须一直存在,通过分离出那些不会被改变的数据,可以把大部分的类数据存储为clean memory,这样才能不断的提高程序的性能。

class_rw_t 的优化

dirty memory在类第一次加载的时候就一直存在,runtime会为它分配额外的内存。运行时分配的存储容量时class_rw_t用于读取-编写数据,但是dirty memory中仍然存在着比较多的clean memory,为了提高空间的利用率,拆分出更多的clean memory,减少dirty memory容量是比奴可少的。
第一步:拆分出class_ro_t,即运行时不被修改的内存。如下图:

类的内存结构优化
提取lass_ro_t

注意:由上图发现一个疑点,为什么方法,属性在class_ro_t中时,class_rw_t还要有方法,属性呢?

  • 属性和方法在运行时中有可能会发生更改,这需要放在class_rw_t中。
  • 在类加载的时候,可以往类中添加属性和方法。
  • class_ro_t只是可读的,需要放在class_rw_t中跟踪类的相关信息。
    第二步:拆分class_rw_t,提取其中的clean memory
    在读取-编写属性和方法的时候,只有10%的类都需要修改或者添加的,那么90%类可以说是不被修改的,那么就可以对class_rw_t进行拆分,拆分如下:

    类的内存结构优化
    class_rw_t拆分

    这样的话class_rw_t的大小就会减少一半,对于真的用到了被拆分出去的数据的时候,可以使用扩展(extension)来完成这些,添加到类中供其使用(大约90%的类不需要这个扩展)如下图:

    类的内存结构优化
    优化后的结构

总结

  • 当有类使用了category的时候,那么此时的类就有了class_rw_t的结构,如果未使用分类,那么类就是一个单纯的class_ro_t的结构。
  • 类结构的优化其实最要是分离出class_ro_tclass_rw_t优化,其实就是对class_rw_t不常用的部分进行了剥离。如果需要用到这部分就从扩展记录中分配一个,滑到类中供其使用。

成员变量/实例变量和属性的区别

《类的探究分析》一文中提到了成员变量存放在class_ ro_t中,那么我们用过查找objc4源码得出下图:

类的内存结构优化
实例变量存放位置

代码层面探究:

@interface XXPerson : NSObject
{
    int hobby;                 //成员变量
    NSObject *objc;            //实例变量
}
@property (nonatomic,strong) NSString *name;                 //属性
@property (nonatomic,strong) NSString *nickName;
@property (nonatomic,assign) int age;
@end

通过xrun编译成main.cpp文件,查看底层代码:

  • xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp (模拟器)
  • xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp (手机)

    类的内存结构优化
    main.cpp分析

    结论:

  • 属性在编译过程中自动加上settergetter方法。
  • 属性在底层编译阶段会变成_方式的成员变量。

补充

官方类型编码

类的内存结构优化
官方类型编码

注意:

  • 编码文档存放在Apple Documents地址
  • Objective-C 不支持long double类型,@encode(long double)返回d,和double类型的编码值一样。
    案例分析:

    类的内存结构优化
    案例分析

    setName(v24@0:8@16)分析结果:

  • vvoid,代表无返回值。
  • 24setName函数的占用字节数。
  • @:参数,id或者self
  • 0:从0号位置开始。
  • :SEL
  • 8:从8号位置开始。
  • @:参数,setName
  • 16:从16号位置开始。

objc_setProperty与copy的关系

objc_setProperty方法相当于一个中间层方法,主要是避免了每个类都调用底层的objc_setProperty方法。当用copy关键字修饰属性时,该属性在编译时候setter方法就会从定向到objc_setProperty方法,不像其他属性·setter·方法使用首地址+内存偏移的方式找到方法实现。
示例代码:

@interface XJPerson : NSObject
@property (nonatomic,copy) NSString *name;         //注意每个属性的关键字
@property (nonatomic,strong) NSString *nickName;
@property (atomic,copy) NSString *address;
@property (atomic) NSString *school;
@end

编译之后查看main.cpp,得到下图:

类的内存结构优化
main.cpp源码结果

结论:

  • 使用copy关键字修饰的属性底层setter方法重定向到objc_setProperty方法
  • 没使用copy关键字修饰的属性底层setter方法通过首地址+内存偏移来寻找并实现。

LLVM验证对象属性为copy时,setter方法的访问

验证流程图:

类的内存结构优化
LLVM验证流程图

LLVM源码流程:objc_setProperty -> getSetPropertyFn -> GetPropertySetFunction -> PropertyImplStrategy -> IsCopy(判断copy关键字)
结论:无论属性是否是原子性还是非原子性的,用到copy关键字修饰的属性setter方法底层都用objc_setProperty实现,strong关键字无法通过最后得判断,需要通过首地址+内存偏移的方式实现。

总结:

底层代码的分析需要很好的耐心,过程也是非常的枯燥。但是当你弄明白了原理之后,就会发现知识是环环相扣的。非常高兴自己又多了一分收获,加油!


推荐阅读
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 20211101CleverTap参与度和分析工具功能平台学习/实践
    1.应用场景主要用于学习CleverTap的使用,该平台主要用于客户保留与参与平台.为客户提供价值.这里接触到的原因,是目前公司用到该平台的服务~2.学习操作 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
author-avatar
wugege12
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有