2012-07-18 43 views
51

ObjectProperties.hObjective-C的协议向声明

@protocol ObjectProperties <NSObject> 

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

@end 

ClassA.h

#import <Foundation/Foundation.h> 

@protocol ObjectProperties; 

@interface ClassA : NSObject <ObjectProperties> 

- (void)specialSauce; 

@end; 

ManagedClassA.h

#import <CoreData/CoreData.h> 

@protocol ObjectProperties; 

@interface ManagedClassA : NSManagedObject <ObjectProperties> 

- (void)doSomething; 

@end; 

从上面的代码示例中,我已经在.h文件中定义了一个协议,以便与Core Data对象以及普通的ol香草对象一起使用。看起来像“噪音”有符合类#进口协议在他们的头;如前所述,在实现文件中转发声明协议&导入会更干净。然而,Xcode中这样做时,这种方式会抛出一个警告:

Cannot find protocol definition for 'ObjectProperties'

代码编译,而且大多作品。我说的主要是因为Core Data试图为标量属性动态创建getter/setters,但我认为这可能是因为我碰到了一个边缘案例。

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

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

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

+0

我有一个类似的问题,但与Swift协议。 – Alper 2018-03-09 11:01:03

回答

93

不能向前声明超类或它是否符合协议。在这些情况下,您必须包含标题。这是因为(在超类的情况下)超类的实例变量和方法成为你的类的一部分;或者(在协议的情况下)协议的方法变成你的类中声明的方法,而不需要明确地声明它们。 (即现在其他人包括你的类的头部会看到你的类声明了这些方法,就好像你自己声明的那样)。唯一可能的方法是如果它已经在这个范围内定义了,即它们的头部被导入。

#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 
4

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

在ARC这是一个错误,因为ARC需要了解的声明的方法与内存管理(做他们返回+1的情况下,内部指针,等等?)

2

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

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

1

由于您声明与协议一致,因此您应该#导入包含该协议的标头。

这是一样的#importing一个超类,而不是向前它声明:

@class Foo; 

@interface Bar : Foo 
// Will get error: attempting to use the forward class 'Foo' as superclass of 'Bar' 
@end;