59

对象属性.h

@protocol ObjectProperties <NSObject>

@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSDate *date;
@property (assign, nonatomic) int64_t index;

@end

类A.h

#import <Foundation/Foundation.h>

@protocol ObjectProperties;

@interface ClassA : NSObject <ObjectProperties>

- (void)specialSauce;

@end;

托管类A.h

#import <CoreData/CoreData.h>

@protocol ObjectProperties;

@interface ManagedClassA : NSManagedObject <ObjectProperties>

- (void)doSomething;

@end;

从上面的代码示例中,我在 .h 文件中定义了一个协议,用于 Core Data 对象和普通的 ol' vanilla 对象。在其标头中包含符合标准的类#import 协议似乎是“噪音”;如上所示,在实现文件中转发声明协议和导入会更干净。但是,Xcode 在这样做时会抛出警告:

Cannot find protocol definition for 'ObjectProperties'

代码可以编译,并且大部分都可以工作。我说主要是因为 Core Data 尝试为标量属性动态创建 getter / setter 时有些奇怪,但我认为这可能是因为我遇到了一个极端情况。

当然,最明显的解决方法是将协议头导入到类头中。

如果我的理解是正确的(并且我的知识是最近获得的,所以我完全有可能是错的),如果我将协议导入我的类头文件并更改协议,那么所有后续文件导入我的类将不得不重新编译。

解决此类问题的正确方法是什么?

4

4 回答 4

113

您不能转发声明它所遵循的超类或协议。在这些情况下,您必须包含标题。这是因为(在超类的情况下)超类的实例变量和方法成为您的类的一部分;或者(在协议的情况下)协议的方法成为您的类中声明的方法,而无需显式声明它们。(即现在包含您的类头文件的其他人将看到您的类声明了这些方法,就好像您自己声明了它们一样。)唯一可能的方法是如果它们已经在此范围内定义,即它们的头文件被导入.

#import <SomeClass.h>
#import <SomeProtocol.h> // these two must be imported

@interface MyClass : SomeClass <SomeProtocol>
@end

前向声明对于仅显示在变量类型(特别是对象指针变量)中的事物很有用。对象指针大小相同,运行时不同类型的对象指针没有区别(对象指针类型的概念只是编译时的东西)。所以没有真正需要确切地知道这些类型的类中有什么。因此它们可以被前向声明。

@class SomeClass;
@protocol SomeProtocol; // you can forward-declare these

@interface MyClass {
    SomeClass *var1;
    id<SomeProtocol> var2;
}
@end
于 2012-07-18T03:17:00.320 回答
4

是的,您是正确的,所有文件都需要重新编译,但这是必要的。导入类头的文件需要知道与其实现的协议相关的方法。如果您将该定义隐藏在 .m 文件中,则它仅对一个文件可见(因为 .m 文件永远不会被导入)。它与前向声明类不同。如果前向声明一个协议,则必须在与前向声明相同的范围内可见的某个地方声明它。我想不出在同一个文件中没有出现这种情况的任何示例。

在 ARC 中这是一个错误,因为 ARC 需要了解已声明方法的内存管理(它们是否返回 +1 实例、内部指针等?)

于 2012-07-18T01:57:57.943 回答
2

通过在 MyClass.h 中声明您的类,您必须导入其超类的所有头文件以及它在 MyClass.h 文件中采用的协议。Objective-c 中的协议被认为是继承的变体类型。

前向声明通常用于类的成员声明。

于 2012-07-18T02:11:02.427 回答
1

由于您声明符合协议,因此您应该 #import 包含协议的标头。

这与 #importing 超类相同,而不是前向声明它:

@class Foo;

@interface Bar : Foo
// Will get error: attempting to use the forward class 'Foo' as superclass of 'Bar'
@end;
于 2012-07-18T02:15:06.590 回答