web-dev-qa-db-ja.com

Objective-Cプロトコルの前方宣言

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;

上記のコード例から、Core Dataオブジェクトと通常のol 'Vanillaオブジェクトの両方で使用されるプロトコルを.hファイルに定義しました。適合クラスをヘッダーにプロトコルを「#インポート」することは「ノイズ」のようです。上で示したように、実装ファイルでプロトコルとimport‬を前方宣言すると、よりクリーンになります。ただし、この方法でXcodeを実行すると警告がスローされます。

Cannot find protocol definition for 'ObjectProperties'

コードがコンパイルされ、ほとんどが機能します。主に言うのは、Core Dataがスカラープロパティのゲッター/セッターを動的に作成しようとするためにファンキーな部分があるためですが、それはおそらくEdgeのケースを見つけたからだと思います。

もちろん、最も明白な回避策は、プロトコルヘッダーをクラスヘッダーにインポートすることです。

私の理解が正しい場合(そして私の知識がごく最近獲得されたので、間違っている可能性が完全にあります)、プロトコルをクラスヘッダーにインポートしてプロトコルを変更すると、クラスを再コンパイルする必要があります。

この種の問題を解決する正しい方法は何ですか?

54
edelaney05

準拠するスーパークラスまたはプロトコルを前方宣言することはできません。これらの場合、ヘッダーを含める必要があります。これは、(スーパークラスの場合)スーパークラスのインスタンス変数とメソッドがクラスの一部になるためです。または(プロトコルの場合)プロトコルのメソッドは、明示的に宣言する必要なく、クラスで宣言されたメソッドになります。 (つまり、クラスのヘッダーを含む他の人は、自分で宣言したかのように、クラスがこれらのメソッドを宣言していることを確認できます。)可能な唯一の方法は、それらがこのスコープで既に定義されている場合、つまりヘッダーがインポートされている場合です。

#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
106
newacct

はい、すべてのファイルを再コンパイルする必要があることは正しいですが、これは必要です。クラスヘッダーをインポートするファイルは、実装するプロトコルに関連付けられたメソッドを知る必要があります。その定義を.mファイルに隠した場合、その定義は1つのファイルにのみ表示されます(.mファイルはインポートされないため)。前方宣言クラスとは異なります。プロトコルを前方宣言する場合、前方宣言と同じスコープ内で見える場所で宣言する必要があります。同じファイル内でこれが発生しない例は考えられません。

ARCでは、これはエラーです。ARCは、宣言されたメソッドのメモリ管理について知る必要があるためです(+1インスタンス、内部ポインタなどを返しますか?)

4
borrrden

MyClass.hでクラスを宣言することにより、スーパークラスのすべてのヘッダーファイルと、MyClass.hファイルで採用したプロトコルをインポートする必要があります。 Objective-cのプロトコルは、継承のバリアントタイプと見なされます。

通常、前方宣言はクラスのメンバー宣言で使用されます。

2
xingzhi.sg

プロトコルへの準拠を宣言しているため、プロトコルを含むヘッダーを#importする必要があります。

これは、スーパークラスを前方宣言するのではなく、#importすることと同じです。

@class Foo;

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