Objective-C协议的前向声明

60

ObjectProperties.h

@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 对象以及普通的对象一起使用。在类文件中导入协议看起来像是“噪音”,在实现文件中进行前向声明并导入协议会更加清晰,就像我上面所展示的那样。然而,这种做法会导致 Xcode 报出警告:

Cannot find protocol definition for 'ObjectProperties'

虽然代码确实编译通过了,并且大部分运行正常。但有时候 Core Data 会尝试动态创建标量属性的 getter / setter,这可能是因为我遇到了某种边缘情况。

当然,最明显的解决方法就是将协议头文件直接导入类头文件。

如果我的理解是正确的(我的知识最近才开始获得,所以完全有可能我是错的),如果我将协议导入我的类头文件并对协议进行更改,则所有后续导入我的类的文件都必须重新编译。

如何才能正确地解决这种问题?


我有一个类似的问题,但是涉及到Swift协议。 - Alper
4个回答

115
您无法前向声明一个超类或其符合的协议。在这些情况下,您必须包含头文件。这是因为(在超类的情况下)超类的实例变量和方法成为您的类的一部分;或者(在协议的情况下),协议的方法成为在您的类中声明的方法,而无需显式声明它们。(即:现在包括您的类头文件的其他人将看到您的类声明了这些方法,就像您自己声明了这些方法一样。)唯一可能实现这一点的方式是如果它们已经在此范围内定义,即它们的头文件已被导入。
#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

既然你要声明符合协议,那么你应该#import包含协议的头文件。

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

@class Foo;

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接