Core Data、宣言されたプロトコル、およびおそらく LLVM 1.5 コンパイラに関連する奇妙な癖に遭遇しています。これが状況です。
IPContainer が IPEvent の親エンティティである IPContainer と IPEvent の 2 つのクラスを持つ Core Data モデルがあります。各エンティティには、mogenerator を使用して作成されたプロジェクト内のカスタム クラスがあります。mogenerator は、モデル化されたプロパティ宣言だけを含む追加のサブクラスを生成するため、実際のクラス階層は IPEvent > _IPEvent > IPContainer > _IPContainer > NSManagedObject です。IPContainer エンティティには、@property(nonatomic, retain) NSNumber* id;
_IPContainer.h で宣言されている「id」という名前の属性があります。_IPContainer.m は@dynamic id;
実装にあり、コア データに実行時にアクセサーを生成するように指示します。
また、プロジェクトで宣言されたプロトコル IPGridViewGroup があり、いくつかのプロパティを定義します。そのうちの 1 つは同じ「id」プロパティです。ただし、このプロトコルを実装するクラスにはセッターは必要ないため、プロトコル内のプロパティは次のように宣言され@property(readonly) NSNumber* id;
ます。 IPEvent クラスは、IPGridViewGroup プロトコルに準拠していることを宣言します。
これは、Clang/LLVM 1.0.x コンパイラ (Xcode 3.2.2 に同梱されているバージョン) を使用すると問題なく動作しましたが、Xcode 3.2.3 と Clang/LLVM 1.5 にアップグレードすると、さまざまなことが変わりました。まず、IPEvent クラスのコンパイル時に次の警告が表示されます。
/Volumes/Ratbert/Users/bwebster/Projects/UberProject/iPhotoLibraryManager/IPGridViewGroup.h:19:31: warning: property 'id' requires method 'id' to be defined - use @synthesize, @dynamic or provide a method implementation
次に、実際にプログラムを実行すると、コンソールに次のように出力されます。
Property 'id' is marked readonly on class 'IPEvent'. Cannot generate a setter method for it.
すぐに続きます:
-[IPEvent setId:]: unrecognized selector sent to instance 0x200483900
また、IPEvent クラスでプロパティを再宣言しようとしましたが、別のコンパイラ警告が表示され、実行時に同じ動作が発生しました。
/Volumes/Ratbert/Users/bwebster/Projects/UberProject/iPhotoLibraryManager/IPManagedObject/IPEvent.h:14:40: warning: property 'id' 'retain' attribute does not match the property inherited from 'IPGridViewGroup'
ここで変更されたのはコンパイラだけなので、変更のきっかけは明らかですが、これが新しいバージョンのコンパイラのバグと見なされるのか、それとも古いバージョンのコンパイラは実際には正しく動作していませんでしたが、新しいバージョンでは、バグがあるのは自分のコードであることが明らかになりました。
だから私がここに持っている質問の中には次のものがあります:
- クラスが読み取り専用プロパティを持つプロトコルに準拠していても問題ないように思われますが、独自の実装でプロパティに読み取り書き込みアクセスを提供しますが、それは正しいですか? ただし、ここでの癖は、 readwrite プロパティが実際には、プロトコルに準拠するクラスのスーパークラスで宣言されていることです。
- コンソール メッセージが Core Data の内部のどこかに出力されていると想定しています。ただし、IPGridViewGroup プロトコルに準拠する場合を除き、IPEvent 自体は「id」プロパティを明示的に宣言しないため、これは奇妙です。ただし、この場合、コンパイラエラーが発生すると思います。これは、AFAIKが通常許可されていない同じプロパティの読み取り専用バージョンで読み取り書き込みプロパティ(_IPContainerスーパークラスで宣言されている)を効果的にオーバーライドするためです。.
- これがコンパイラのバグであれば問題ありません。今のところ、いくつかの異なる方法で回避できます。ただし、コンパイラがここで正しいことを行っている場合、コンパイラの警告や実行時エラーが発生しないように、これらすべてを整理する方法を考え出すのに途方に暮れています。
編集:回避策は、IPEvent クラスでプロパティを再度宣言することですが、コンパイラの 2 つのバージョンの動作が異なる理由についてはまだ戸惑っています。また、プロトコルで宣言されたプロパティが、クラスで宣言されたプロパティとどのように相互作用するかについても不明です。
クラスで (プロトコルではなく) 読み取り専用プロパティを宣言し、読み取り書き込みプロパティをオーバーライドすると、「警告: プロパティ 'longitude' の属性 'readonly' は、'_IPEvent' から継承されたプロパティの属性 'readwrite' を制限します」というメッセージが表示されます。プロトコルで宣言しても同じ効果がある場合は、コンパイラから同様の警告が表示されるはずです。
ただし、直感的には、IPEvent はプロパティに必要なゲッターを既に実装しているため、たまたまプロパティのセッターも実装していたとしても、それは「プロトコルに準拠している」と見なされるはずです。