2009-12-15 36 views
7

我不知道为什么,但有时我已经成功地解决一些由.h文件移动到#import "someClass.h" .m文件编译错误,最显着的可可:导入头文件和导入主文件有什么区别?

error expected specifier-qualifier-list before 'someClass' 

。这也与我遇到的一些其他问题一起工作,这些问题与标题相关(从我的观点来看是神秘的)。

一些粗略的谷歌搜索出现了答案:“从不在头文件中导入头文件”,这就是通知停止的地方。

要么我已经完全做到了这一点,或者我已经从某个地方拿起了这个习惯,但是我认为这个头文件是头文件被导入的地方。显然不是,但任何人都可以向我解释为什么是这样的,导入头文件的首选方式是什么?

回答

10

除非您从包含的类继承,否则不应在标题中包含标题。如果您需要包含一个对象作为接口变量,则应该使用@class指令代替;这会告诉编译器该标识符引用了一个类。

相反,只在实现文件中导入标题。编译器会知道你的实例变量是指向对象的指针,但是在解析头文件时它不知道对象的细节。它需要知道的是它是一个班级。编译器可以在解析实现文件时看到该类的方法;此时,它确实需要该类,以验证它是否响应了您要发送的消息。


更新:我会更新我的回答一些问题后回应,但Rob Napier has a good follow-up

+1

'typedef's和'protocols'怎么样? – Joost

+0

你不会碰巧知道一个教程,或者使用它的代码示例? – gargantuan

13

约翰给出了很好的建议,但这里有更多的背景,关于whys,因为和例外。

避免将标题导入标题有两个目的:改进增量构建时间和避免循环依赖。如果导入A.hB.h并导入B.hC.h,然后每次在A.h改变任何东西的时候,你必须重新编译C.m,即使C.m是没有使用任何的A.h定义的东西。这可能会导致非常可怕和不必要的构建流失,特别是如果您的头文件经常更改(如在开发的早期阶段常见)。

第一个目标值得称赞,但对于小型项目,谁在乎?你有一个四核Pro,一个完整的构建需要几分钟,对吧?但是你仍然需要担心第二个问题:循环依赖。 A.h参考类别BB.h参考类别A。这实际上可能经常发生,并且可能非常无辜地进入系统。集合对象可能会引用其包含的对象的类型,并且对象可能会引用集合对象的类型。它只需要一个引用,因为某些方法需要或返回该类型。如果您有标题导入其他标题,则发生这种情况的可能性会迅速接近统一。你用递归导入结束了,编译时错误可能令人兴奋。 “我知道,typdef被定义了!它就在那里!它是进口的!“但是,当你导入这个头文件时,它还没有被解析,这是什么导致你的错误如上所述

因为这个原因,即使你不关心构建时间你应该),避免导入标题变为头......除了....

有些头你进口。你当然父。文件定义@protocol你实现或typedef使用。所以,是的,你必须包括这些。

那么系统头怎么样?那么,他们永远不会去c因为他们不会引起递归进口,所以他们没事。我不鼓励人们在系统头文件中使用@class转发声明。它为您的标题的用户创造了额外的工作,没有任何价值。要获得良好的标题卫生,请记住将系统标题放在<尖括号>中,并将标题放在“引号”中。

所以这不是一个小问题,但简单的规则是:避免在编译器允许的任何时候将用户头文件导入到其他用户头文件中。

+0

非常感谢你。 – gargantuan

0

您不仅将头文件包含在实现文件中,还包含到使用该类的其他文件中。如果你在头文件中包含了所有的依赖项,那么包含该头文件的所有其他文件仅仅用于使用它定义的特定类,它们本身也会包含所有依赖项。

这不仅嘈杂,但当你有类引用自己的时候会导致问题。每个类都必须在另一个类之前导入,这导致先导入一个类,然后再找不到另一个类的类型。这会导致您发布的模糊错误消息,这基本上意味着编译器无法找到someClass类型。

通过将您的导入移动到您的实现文件中,并在头文件中正向声明类和类型(使用@class@protocol等),可以避免这些问题。

相关问题