@synthesize with implicit ivar: strange inheritance behavior
When I try to compile the code below I get build failed with "baseClassVar" undeclared (first use in this function)
at the line marked "error occurs here".
(baseClassVar is an ivar in the superclass, which should be visible to the subclass.)
But if I uncomment the explicit ivar declaration for "subclassVar" (line 11), the code compiles without complaint.
In other words, if the subclass has an implicit ivar, it can't see the (unrelated) explicit ivars of the superclass!
What the?
Any idea what's going on here?
Code:
@interface BaseClass : NSObject {
NSString *baseClassVar;
}
@end
@implementation BaseClass
@end
@interface Subclass : BaseClass {
//NSString *subclassVar; // <-- uncomment this and it compiles fine
}
@property (nonatomic, retain) NSString *subclassVar;
@end
@implementation Subclass
@synthesize subclassVar;
- (void)aMethod {
baseClassVar = @"something"; // <-- error occurs here: "baseClassVar" undeclared (first use in this function)
}
@end
1. I was looking for protected behavior, ie, I want a value that's readonly from the outside but readwrite to subclasses. AFAICT after searching, this can't be done with properties.
2. If you look again you'll see there are two ivars, an explicit one (baseClassVar) in BaseClass and an implicit one (subclassVar) in Subclass. The complier stops complaining about the BaseClass ivar when I change the Subclass ivar from implicit to explicit.
4. See #2: Again I think you're confusing the two ivars. The property subclassVar, declared in Subclass, should have nothing to do with the ivar baseclassVar, declared in BaseClass.
5. Actually I would prefer to use only properties, but I don't like how that pretty much forces you to make all your values public readwrite. (What is a readonly property anyway if you never directly reference the underlying ivar? How do you set it?)
but
3. Actually, this is a real solution. It turns out that changing baseClassVar = @"something" to self->baseClassVar = @"something" makes the code compile.
I confirmed that this only fails using GCC 4.2; LLVM GCC and LLVM 2.0 works fine on this situation. I don't think there's any reason to use GCC 4.2 anymore, all issues regarding older devices are fixed since XCode 4.0.1
BTW You can re-define properties in a private category, allowing you to make it publicly readonly, but privately readwrite, like this (compiles fine on either GCC4.2 or newer compilers)
Yes, you're right the LLVM compiler doesn't choke on this. Thanks!
And yes, redefining properties in a private category looks like the way to go. But if you want protected properties, ie properties that subclasses can write, you have to put them in a separate header file. Each subclass (and the base class implementation file!) will have to import this new header file and also re-@synthesize the property.
This seems like a lot of work to get properties to do something ivars do for free.
Or I suppose you could redo the @property declarations in every subclass that needs to access them. Then you avoid the extra BaseClass+Protected.h header file, the tradeoff being you have to rewrite the @property declarations in every subclass (and the @synthesize statements too, but you were rewriting those already).
It's disheartening that so much bookkeeping is required just to accomplish what the keyword "protected" provides in java. Is anybody really doing this when they write subclasses? My guess is they either access the ivars directly or they've given up on protecting their properties at all, and code everything readwrite.
Oh, I see. I have used the ivar approach for subclasses too.
I'm not sure declaring it in another file would even work, if you override the property anywhere outside of the main class implementation wouldn't it require a separate @implementation for the category, that would in turn need to access the ivar directly? Too much trouble indeed