10

关于 Apple 的技术问答:http: //developer.apple.com/library/mac/#qa/qa1490/_index.html

我认为编译器可以在编译时标记对类别中定义的方法的调用(它知道它们是在类别中定义的,而不是在主类中,因为原型在一个@interface Class (Category)部分中) - 所以它可以在目标文件中构建一个表“外部类别方法”。然后,链接器在进行正常链接之后,应该能够连接/合并和处理来自所有对象的“外部类别方法”表,并在所有链接的框架/库/对象的匹配类类别中查找匹配符号,然后它可以拉入尚未“进入”目标的那些。

一定有什么我错过了,但它是什么?为什么这是不可能的?

4

1 回答 1

14

链接器将静态库视为一大堆旧的随机片段集合,它将从中提取单个片段来满足来自链接单元其余部分的任何符号请求。

即如果主程序调用_foo并且_foo只出现在静态库中,那么 将_foo连同任何依赖符号一起被拖入。

但是,当您调用类别中的方法时,由于 Objective-C 的动态性,没有特定的符号引用。

-ObjC标志告诉链接器,因此,它应该从静态库中获取所有类别并将它们放入主二进制文件中。


这有点令人困惑,并且假设编译器应该对此更聪明(并且可以肯定的是,它应该在开发工具级别提供帮助)。重要的是要记住几件事:

  • 在链接器滚动时,头文件中声明的任何内容都几乎丢失了。符号是由编译单元创建的,而不是由头文件创建的。头文件几乎生成了一个符号将在以后具体创建或由链接实现的承诺,但不能在其自身中创建符号(否则每个编译单元 - 每个 .o - 最终都会得到一个副本符号和欢闹将在链接时随之而来)。

  • Objective-C 是完全动态的。当您说[(id)foo bar];时,唯一的要求是在之前的某个地方bar定义。它是否真正实现并不重要(直到运行时)。

  • 类别不必有对应的@implementations;类别可用于声明可能存在的方法,事实上,在添加@optional到之前@protocol,通常在NSObject(ewwwwww) 上使用类别而不@implementation说“嘿,此可选方法可能在运行时存在”。

  • 编译和链接是完全独立的过程。编译就是扩展代码并将其转换为可执行字节库。链接就是获取这些库并将它们组合成可以实际运行的东西,包括解决库之间的所有依赖关系。编译器并不真正知道如何链接某些东西,并且链接器没有任何关于可能已经定义的东西(没有产生硬符号)的信息。

最终结果?

链接器没有足够的信息来解决依赖关系。

于 2012-06-18T22:55:20.397 回答