views:

43

answers:

2

I'm getting this warning. What I'm trying to do is have a family of classes and a parallel family of protocols. The class Piece has a declaration like this:

@interface Piece : NSManagedObject <PieceModel>
{
}

...

@property (nonatomic, retain) Player *owner;

...

@end

PieceModel has this

@protocol PieceModel <NSObject>

...

@property (readonly, nonatomic, retain) id <PlayerModel> owner;

@end

And of course:

@interface Player : NSManagedObject <PlayerModel> { ...

It seems to me this should all be totally safe. Users of the protocols see that something conforming to the PieceModel protocol has an owner that should conform to the PlayerModel protocol. And in fact, every instance of the Piece class returns a Player instance for the owner property, which conforms to the PlayerModel protocol. I do see why there is such a warning. It would not be so safe to try to assign any object that conforms to PlayerModel to owner, since it might not belong to the Player class, but that is not a problem in this case because the property is declared as readonly for the protocol.

Notice I also declared the property as retain, which if I am not mistaken is meaningless for a readonly property, but I also got a different warning about a mismatch between the protocol and the class if I didn't do that. At least the compiler does not complain that one property is readonly and the other is not.

I know I could just declare the class property as returning id <PlayerModel>, but that would be undesirable for a couple reasons. Users of Piece objects that have them statically typed as Pieces would have to do a cast to get something statically typed as a Player. Also, I would have to write the property implementation myself instead of just using @synthesize, or in this case actually @dynamic; Core Data generates the property implementations.

So, can I instruct the compiler to suppress this warning? Or is there a better way to write this code that won't generate the warning?

A: 

This generates no warnings ...

@protocol PlayerModel <NSObject>
@end

@protocol PieceModel <NSObject>
- (id<PlayerModel>)owner;
@end

@interface Player : NSObject <PlayerModel> {
}
@end

@interface Piece : NSObject <PieceModel> {
}
@property (nonatomic,retain) Player* owner;
@end

You will then of course not be able to use @synthesize for PieceModel.owner, but that's not so much extra work. Remember that @property declarations are basically just short hand for declaring the setter and getter and defining the behavior of methods generated by @synthesize.

Also keep in mind that dot notation for accessing properties is just syntactic sugar, so if you're fond of dot notation, you'll still be able to use it for accessing 'owner' on variables declared as id<PieceModel>.

imaginaryboy
Nope, then I get:warning: class 'Piece' does not fully implement the 'PieceModel' protocolwarning: method definition for '-owner' not foundwarning: incomplete implementation of class 'Piece'`- (id <PlayerModel>)owner;` is the same as `@property (readonly) id <PlayerModel> owner;`, but the compiler doesn't seem to be smart enough to figure that out in the case of a class conforming to a protocol.Even if I declare PieceModel like: @protocol PieceModel <NSObject> -(Player *)owner; @endwhich I don't want to do, I get the same warnings.
Greg
Hmm, I tried something else and it seems like your solution works but not with the @dynamic definition of the property.
Greg
Correct. My suggestion specifically avoids declaring the @property in the PieceModel protocol.
imaginaryboy
A: 

Is owner a relationship in your data model? If so, you might find the compiler is confused because NSManagedObject needs to respond to it.

Otherwise, it looks like a limitation of the way properties are handled in subclasses or implementations of protocols. If you replace NSManagedObject by NSObject in Piece and Player and you still get the issue, it might be worth reporting a bug to Apple.

As a work around for the issue, I think you should not declare the property in Piece and declare a separate setter for owner i.e.

@interface Piece : NSManagedObject <PieceModel>
{
}
...

//@property (readonly, nonatomic, retain) id<PlayerModel> owner;
// property declaration not needed because it's in the protocol

-(void) setOwner: (Player*) newOwner; 
...

@end

and implement the setter manually.

On an unrelated note, I wouldn't bother declaring properties as nonatomic ever unless I had evidence from a profiler that it provides a significant performance boost.

JeremyP
Yes, owner is a relationship in the data model. It seems that might be part of the problem. The suggestion by imaginaryboy gets rid of warnings related to this issue if I provide an implementation of the accessors like this: -(Player *)owner { ... } -(void)setOwner:(Player *)owner { ... }It doesn't work if I use the @dynamic keyword to indicate Core Data will provide the accessors. The compiler isn't aware that the proper accessor will exist. But isn't @dynamic supposed to inform the compiler that the class will respond to selectors exactly like the above?
Greg
I tried your suggestion and it does get rid of the warnings, but there are some drawbacks. First, in theory, code that has a statically typed Piece object might want to access the owner as a Player, not a PlayerModel, without a cast.Secondly, there is the part about writing the setter myself, which I could do, but I'd rather not for this and several other classes that I have a similar problem with. It works as is; I'm really just trying to get rid of the dozens of warnings I get. These spurious warnings make it easy for me to miss significant warnings.
Greg
Well I'm sorry but you seem to be asking for the language to do things it can't. Frankly I think your idea of mirroring your data model with protocols is misguided. Every time you change your data model, you have to manually change the affected protocols as well as your custom managed objects. It sounds like a maintenance nightmare.
JeremyP

related questions