架构技术评论

常量字符串,你可能不知道的那些事儿~

大家好,我是TF,目前就职于阿里巴巴,从事iOS开发多年。这是我第一篇公众号文章,未来我会持续不定期更新,欢迎大家关注我,一起学习,一起探讨,一起成长。
今天的主题是关于常量字符串,下面直接进入正题!




一、常量字符串在macho文件中是怎么存储的?

所有的代码都是存在代码段的,也就是machO文件的TEXT段。

那么字符串应该也是存在这个segment中的。我们用hopper工具来看一下。


Section __cstring段:


Section __ustring段:




二、运行时字符串是如何使用的呢? 下面会把解题过程拆成一步一步的介绍,跟紧我的思路哦~
第一步:先看源码字符串的定义

Demo中的源码是如下定义的:

- (void)viewDidLoad {    [super viewDidLoad];    NSString *str = @"abcd";}

第二步:将.m文件转成.cpp文件
源码中的字符串str变量,变成了 __NSConstantStringImpl__var_folders_l7_r5syspkx43724t__mz8h8rr00000gp_T_ViewController_754f19_mi_0 这个变量。
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));    NSString *str = (NSString *)&__NSConstantStringImpl__var_folders_l7_r5syspkx43724t__mz8h8rr00000gp_T_ViewController_754f19_mi_0;}

__NSConstantStringImpl__var_folders_l7_r5syspkx43724t__mz8h8rr00000gp_T_ViewController_754f19_mi_0 变量的定义如下:
 static  __NSConstantStringImpl  __NSConstantStringImpl__var_folders_l7_r5syspkx43724t__mz8h8rr00000gp_T_ViewController_754f19_mi_0  __attribute__ ((section ("__DATA, __cfstring"))) =  { __CFConstantStringClassReference,   0x000007c8,   "abcd",   4};

__NSConstantStringImpl的定义

#ifndef __NSCONSTANTSTRINGIMPLstruct __NSConstantStringImpl {  int *isa;  int flags;  char *str;#if _WIN64  long long length;#else  long length;#endif};

第三步:__NSConstantStringImpl 中char*str这个属性存的是什么?
通过上面“变量的定义”中可以看到,变量是存在__DATA 段的__cfstring中的。

通过machoview这个工具,确实有__cfstring section(下图中1 所指)。 在这个section中存的是一个数组,数组中的元素有4个字段(下图中2所指),与上述__NSConstantStringImpl的定义相符。


其中第三个属性存储的value是一个地址,如下图。



第四步:看下0x100007240是指向哪里的

TEXT的__cstring中第一个字符串的offset就是7240.(此处0x100007240,第一位多一个1,啥原因以前看过,忘记了😓)


以上就是字符串在运行时是如何找到这个字符串的过程了。


第五步:通过下图来总结一下



基于以上的内容,我们猜测,运行时获取到的字符串的地址减去内存镜像基址,应该就是__NSConstantStringImpl这个类型数据相对于macho文件的偏移地址。



三、Demo验证猜测

字符串的地址 - 内存镜像基址 = 10008008。


与macho文件中,第一个字符串的offset一致。




四、总结
  • 常量字符串存在TEXT段的__cstring和__ustring中。在DATA段__cfstring中存着一个数组,数组中的元素有一个属性是指向__cstring和__ustring这两个段的字符串的地址的。

  • 在运行时:字符串的地址 - 内存镜像基址 的地址是固定的。



知识点比较简单,大家可以尝试自己验证一下,那么什么场景可以用到呢?TF开了一个脑洞,想到做常量字符串压缩。大家可以发挥想象,看看能用到什么其他的场景。


关于具体的代码实现,这里不急着发出来,大家可以先尝试写一下。自己动手的过程,理解的会更加深入。



记得关注、转发、分享哦,我后面发Demo源码的哦!比心!~~~