如何在非脏内存中创建链接器解析的数据表

Posted

技术标签:

【中文标题】如何在非脏内存中创建链接器解析的数据表【英文标题】:How to create a table of linker-resolved data in non-dirty memory 【发布时间】:2017-10-10 17:43:13 【问题描述】:

我想创建一个数据表,并将其保存在非脏内存中(这样该表就不会影响 ios 和相关平台 (tvOS/watchOS) 上的应用程序的内存使用量)。

表格是一个包含两条数据的数组:Objective-C 类和一个数值:

#include <Foundation/Foundation.h>
struct TypeMap 
    Class class;
    int value;
;

我想做这样的事情:

struct TypeMap map [] = 
     [NSObject class], 0x1234 
;

但这显然不起作用,clang 抱怨:

test.m:9:4: error: initializer element is not a compile-time constant
         [NSObject class], 0x1234 
          ^~~~~~~~~~~~~~~~

这当然是完全有道理的,因为[NSObject class] 不是编译时常量。

但是有一个动态加载器能够解析的符号:_OBJC_CLASS_$_NSObject,这让我想到了这样的事情:

extern Class OBJC_CLASS_$_NSObject;
struct TypeMap map [] = 
     OBJC_CLASS_$_NSObject, 0x1234 
;

这个想法是动态链接器可以在运行时解析符号,然后将内存标记为只读(与普通代码的工作方式相同)。

不幸的是它遇到了同样的问题:

test.m:11:4: error: initializer element is not a compile-time constant
         OBJC_CLASS_$_NSObject, 0x1234 
          ^~~~~~~~~~~~~~~~~~~~~

我确信我可以用汇编代码表达这一点,但我想尽可能避免汇编并坚持使用 Objective-C(无需每个平台实现一次)。

我在这里完全偏离轨道了吗?这甚至可能吗?

更新

工作版本:

// clang test.m -framework Foundation
#include <Foundation/Foundation.h>
#include <objc/objc.h>
#include <objc/runtime.h>

struct TypeMap 
    Class class;
    int value;
;

extern void* OBJC_CLASS_$_NSObject;
const struct TypeMap map [] = 
     (Class) &OBJC_CLASS_$_NSObject, 0x1234 ,
;

int main ()

    printf ("%s %p %i\n", class_getName (map[0].class), map [0].class, map [0].value);
    return 0;

【问题讨论】:

c-标签是有原因的,还是您只是觉得它美观? 我不熟悉“非脏内存”这个词。在上下文中,我相信您的意思是“只读数据段”,但我不是 100% 确定。请澄清。 @zwol:我所说的非脏内存是指 iOS 可以分页的内存(并且不会计入应用程序的内存限制)。请参阅***.com/a/19238896/183422 以获得更深入的解释。 【参考方案1】:

如果我理解正确的话,Objective-C 中的 Class 是 aggregate type,在 C 标准使用该术语的意义上。那么,给定

struct TypeMap 
    Class class;
    int value;
;

extern Class OBJC_CLASS_$_NSObject;
struct TypeMap map [] = 
     OBJC_CLASS_$_NSObject, 0x1234 
;

您要求动态加载器在加载时将聚合复制到您的数据结构中,这不是它所具有的功能。

您应该能够做的是让您的 TypeMap 包含 指针OBJC_CLASS_$_... 符号:

struct TypeMap 
    Class *class;
    int value;
;

extern Class OBJC_CLASS_$_NSObject;
const struct TypeMap map[] = 
     &OBJC_CLASS_$_NSObject, 0x1234 ,
    // ...
;

试一试,看看效果如何。

(注意在map 的声明中添加的const - 您需要首先将此数据结构放入只读数据段中。)

【讨论】:

答案不是 100% 正确,因为符号似乎是聚合数据。这意味着这是工作版本:struct TypeMap Class class; int value; ; extern void* OBJC_CLASS_$_NSObject; struct TypeMap map [] = (Class) &amp;OBJC_CLASS_$_NSObject, 0x1234 , ; 进一步的测试表明,虽然它可以编译,但它并没有解决我的问题:仪器显示数据最终成为脏内存。如果我用代码编写它(一个带有巨大 switch 语句的方法),那么它最终不会成为脏内存,所以这在技术上显然是可行的。 @RolfBjarneKvinge 对不起,我对 Objective-C 或 iOS 编程环境的了解还不够,无法进一步帮助您。您应该提出一个新问题,突出您的“struct TypeMap”和巨型开关语句方法之间的对比。

以上是关于如何在非脏内存中创建链接器解析的数据表的主要内容,如果未能解决你的问题,请参考以下文章

链接器链接过程及相关概念解析

如何制作函数装饰器并将它们链接在一起?

如何在 C 中创建 AT 命令解析器以获取来自 USART1 的传入字符串?

如何在 Flutter 中创建像 Ajio 移动应用一样的加载器动画。下面是装载机的视频

在 SML Alice 中创建中缀/后缀/前缀解析器

Java类加载器