【问题标题】:Where and how are an Objective-C class's methods stored?Objective-C 类的方法存储在哪里以及如何存储?
【发布时间】:2013-11-18 01:56:54
【问题描述】:

我知道当一个对象在堆上实例化时,至少分配了足够的内存来保存对象的 ivars。我的问题是关于编译器如何存储方法。内存中是否只有一个方法代码实例?还是代码在内存中生成对象的固有部分,与 ivars 连续存储并执行?

如果是后者,即使是像 NSStrings 这样的微不足道的对象也需要(相对)大量的内存(NSString 也继承自 NSObject 的方法)。

或者该方法是否在内存中存储一​​次并传递一个指向拥有它的对象的指针?

【问题讨论】:

  • 每个对象(至少是普通对象)都有一个类。有一个从对象指向其类对象的指针(当然,它有自己的指向类对象的指针,但我们不会去那里)。类对象由该类的所有对象共享,并且类的方法与类对象绑定。
  • 你为什么关心这个?
  • 你要明白的是,对象在内存中的存储方式是一个实现细节。 Apple 可以选择将每个对象设为 64 位整数数组,并将每个方法设为指向一些随机 OS 代码块的指针,以供您关心。您只需要担心 Objective-C 是否按照包装盒上的说明进行操作。
  • @BryanChen:好奇(特别是关于你的工具如何工作)可能是程序员可以拥有的最重要的特质。你为什么关心他为什么好奇?
  • 我的意思是它是实现细节。大多数情况下,您的代码不应依赖于实现细节,因为它们可能会发生变化。除非您遇到严重的性能问题或发现(潜在)错误,否则您应该能够在不了解这些知识的情况下编写代码。

标签: objective-c methods objective-c-runtime


【解决方案1】:

在“标准”Objective-C 运行时中,每个对象在任何其他实例变量之前都包含一个指向它所属的类的指针,就好像基 Object 类有一个名为的实例变量:

Class isa;

给定类的每个对象共享相同的isa 指针。

该类包含许多元素,包括一个指向父类的指针,以及一个方法列表数组。这些方法是专门在这个类上实现的。

struct objc_class {
    Class super_class;
    ...
    struct objc_method_list **methodLists;
    ...
};

这些方法列表每个都包含一个方法数组:

struct objc_method_list {
    int method_count;
    struct objc_method method_list[];
};

struct objc_method {
    SEL method_name;
    char *method_types;
    IMP method_imp;
};

这里的IMP 类型是一个函数指针。它指向内存中存储方法实现的(单个)位置,就像任何其他代码一样。


注意:我在这里描述的实际上是 ObjC 1.0 运行时。当前版本并没有像这样存储类和对象;它做了许多复杂而聪明的事情来使方法调用更快。但我所描述的仍然是其工作方式的精神,即使不是它的确切方式。

我还在其中一些结构中遗漏了一些字段,这些字段只会混淆情况(例如,向后兼容性和/或填充)。如果您想查看所有血淋淋的细节,请阅读真正的标题。

【讨论】:

  • 更高版本的 ObjC 可以使用 vTable 优化一些常见的(Apple 通用的)方法调用。大多数其他人过去常常在 SEL 缓存中查找并在 2.0 中返回。最新版本的运行时中所有这些新的魔法 isa 东西可能会改变这种情况。
  • 哦,好吧,只是关于 ObjC 运行时的后续问题:如果内存中的 Class 仅包含其自己的方法数组,则继承的方法实际上是通过实际向上移动超类链执行的,直到找到了吗?对汇编中的过程编程有一点了解,我对如何在运行时管理对象和类的低级细节非常着迷。
  • @MichaelZimmerman:是的,这正是它的工作原理。如果类的任何列表中都不存在方法,则运行时将继续在 super_class 中搜索。
  • 虽然实际上为了速度,Apple 使用选择器映射缓存,因此运行时不必在每次进行方法调用时手动搜索表。有趣的是,在过去,Object Pascal 和 Think C(“with objects”)最初对每个方法调用进行表搜索,这使得函数调度变得非常慢。
  • 正确 - 正如我的答案底部所述,为了简单起见,我正在描述原始的 ObjC 运行时。
【解决方案2】:

方法在内存中存储一​​次。根据平台的不同,它们会根据需要被分页到 RAM 中。如果您真的想了解更多详细信息,请阅读 Apple 的 Mach-O 和运行时指南。这通常不是程序员自己关心的事情,除非他们正在做一些非常低级的事情。

对象并不真正“拥有”方法。我想你可以把它想象成拥有方法的类,所以如果你有 400 个 NSString,你在 RAM 中仍然只有每个方法的一个副本。

当一个方法被调用时,第一个参数是对象指针,self。这就是方法如何知道它需要操作的数据在哪里。

【讨论】:

  • -1 Objective-C 的方法分派与 C++ 不同。您不能只将其留在“他们被转储到 RAM 中”。
  • 非常感谢!这很有道理。
  • 其实 Objective-C 的方法调度与 C++ 非常相似。最大的区别是方法指针在运行时更动态地确定,而不是通过索引表查找。
  • @EricS ObjC 方法分派与 C++(运行时分派与编译时分派)相反。 is 有点像虚函数,但差别还是很大的。
  • 我不会这么说。它们本质上都做同样的事情,将类+方法映射到函数指针,但是 Objective-C 更加动态和灵活,而 C++ 更快。请注意,在 OOP 流行之前,我写了很多代码,我们过去常常使用函数指针表自己做类似的事情。很酷的优势是我们可以逐个对象而不是整个类来覆盖方法。
猜你喜欢
  • 2023-03-14
  • 2021-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-13
  • 2011-10-30
  • 2011-01-05
  • 2012-03-22
相关资源
最近更新 更多