【问题标题】:#import does not protect against circular calls?#import 不能防止循环调用?
【发布时间】:2011-09-29 19:15:06
【问题描述】:

我有两个类 ClassA 和 Class B(它们是 viewControllers)。
A 类是 B 类的代表。
ClassA“laucnhes”和 ClassB 的实例。
ClassB 调用 classA 上的方法。

假设是:

#import "ClassB.h"

@interface ClassA : NSObject {
    ClassB* subController;
}

- (void) doThis;

-------------------------------

#import "ClassA.h"

@interface ClassB : NSObject {
    ClassA* delegate;
}

-------------------------------

@implementation ClassB

- (void) someMethod {
   AnObject* myObj = [self.delegate.arr objectAtIndex:8];
   [self.delegate doThis];
}

这样一来,A 必须导入 B,B 必须导入 A。

如果 B 不导入 A(仅使用 @class A),则从 A 中使用的属性存在编译错误。
如果 B 导入 A,ClassA* delegateline 上会出现编译错误。

为什么会有这些编译错误? #import 不会再次保护递归调用吗?

我不需要解决方案来解决这个问题,我知道该怎么做。
但我想知道为什么我的#import 会导致这些问题。这些不是#includes...

【问题讨论】:

  • 您是将@class ClassA 放在ClassB.h 还是.m 中?
  • @outil:没有。但是我在#import 上的问题上犯了一个错误。问题已编辑以更正。
  • 什么是编译错误,请贴出来?

标签: objective-c import header include


【解决方案1】:

在.h 文件中更喜欢@class 而不是#import。然后可以将两者都导入到 .m 实现文件中。

// ClassA.h -------------------------------
@class ClassB;
@interface ClassA : NSObject {
    ClassB* subController;
}

- (void) doThis;

// ClassB.h -------------------------------
@class ClassA;
@interface ClassB : NSObject {
    ClassA* delegate;
}

// ClassB.m -------------------------------
#import "ClassA.h"
#import "ClassB.h"

@implementation ClassB

- (void) someMethod {
   AnObject* myObj = [self.delegate.arr objectAtIndex:8];
   [self.delegate doThis];
}

使用@class 语句而不是#import 还可以减少依赖关系并使其余的更清晰。它还可以加快编译时间。

【讨论】:

  • 对编译时间存疑。如果你向前声明这个类,你最终会包含它的标题。无论您是现在读取和处理该文件并稍后从缓存中读取它,还是稍后再读取它,都将是非常小的差异..
  • @Nektarios 你是对的,最终你必须编译头文件,但是由于#import 将所有文本从头文件粘贴到目标位置,每次导入都会额外编译一个头文件时间。对于最小的标头来说不是什么大问题,但对于多次导入的复杂文件来说可能是一个问题。
  • @CocoaFu :我知道它更喜欢那样,但这不是问题。我知道。但是#import 应该可以防止这种类型的递归调用,但它似乎没有。
  • 文件不包含递归,这就是为什么不包含文件,因此看不到声明并且存在编译器错误。但是这里有猜测,因为问题中没有给出错误。
  • @Nektarios:在大型项目中,使用前向声明可以显着缩短编译时间。如果ClassA 有一个ClassB* 成员并且转发声明ClassB,则任何导入ClassA.h 但不需要使用任何ClassB 内容的文件都可以避免拉入ClassB.h(以及任何也导入的标头)。
【解决方案2】:

为什么会有这些编译错误? #import 不会再次保护递归调用吗?

#import 防止重复将相同的标头导入相同的模块,无论是否通过循环包含/导入。它通过不让您这样做来防止这种情况:只有标题的第一个 #import 有效;相同标头的后续#imports 将被忽略。

在循环#include 的情况下,预处理器会绕圈数次,然后在编译之前使构建失败。使用#import 可以防止预处理器陷入困境并让预处理器成功,但循环-#import 代码充其量仍然是狡猾的,通常不会编译。

那么,根据你的具体情况。

对于您在问题中显示的代码,@class 将在一个或两个标题中工作,实际上您应该在两个标题中都使用它。您还需要 #import 两个 .m 文件中的两个标头。

如果 B 不导入 A(仅使用 @class A),则从 A 中使用的属性存在编译错误。

如果你的意思是“在我使用 ClassA * 类型的属性的每个点都有一个编译错误”,那么是的:你不能与那个对象对话,因为你没有导入它的接口,所以编译器不知道您可以向ClassA 实例发送哪些消息。这就是为什么你需要导入它的接口。

如果 B 导入 A,则 ClassA* 委托线上会出现编译错误。

如果两个标题相互导入,那么你有这个:

ClassA.m:
    ClassA.h
        ClassB.h
            ClassA.h (ignored because this was already imported by ClassA.m)
ClassB.m:
    ClassB.h
        ClassA.h
            ClassB.h (ignored because this was already imported by ClassB.m)

如果没有一个接口在另一个接口之前没有另一个接口在它之前,这是不可能工作的。这就是你遇到的圈子——#import 的存在就是为了打破这个圈子。 #include 允许圆圈,从而被楔入:

ClassA.m:
    ClassA.h
        ClassB.h
            ClassA.h
                ClassB.h
                    ClassA.h
                        ClassB.h
                            ClassA.h
                                ClassB.h
                                    ClassA.h
                                        ClassB.h
                                            ClassA.h
                                                ClassB.h
                                                    (fails at some point)

因此#import

所以你不能从另一个导入每个标题。因此@class

但是您仍然需要从每个模块中导入每个标头。实际上,这正是您需要做的:在每个标头中使用 @class,在每个模块中使用 #import(在 both 标头上)。

【讨论】:

    【解决方案3】:

    可以通过声明来避免这种编译投诉

    @class ClassB;
    

    在 .h 文件中。然后可以将 ClassB.h 包含到 .m 文件中。

    所以你是对的。与都市神话相反,#imports 的工作方式与 #includes 非常相似,因为编译器必须检查文件。

    请参阅this(重复?)问题以解决您的哲学问题。

    【讨论】:

    • #include 和#import 的主要区别在于#import 只会发生一次,不需要保护定义。当然#import 对这个问题没有帮助。
    • @Mundi:我看不到我的哲学问题的答案就是问题。为什么递归调用会产生复制错误?我已经阅读了您指出的问题,但这无济于事。
    【解决方案4】:

    我想您会发现 #import 仅在它已成功包含一次后才能防止多次包含,可以这么说。

    即,在您的情况下,它在被要求再次导入之前没有成功导入 classa.h,所以它这样做了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-06-17
      • 2018-03-15
      • 1970-01-01
      • 2021-11-03
      • 1970-01-01
      • 2016-09-01
      • 2015-07-30
      相关资源
      最近更新 更多