If a subclass refers to a superclass ivar, synthesizing an unrelated property fails
As replied in this question : objective c xcode 4.0.2: subclass can't access superclass variables "was not declared in this scope"
From the doc : Apple Objective-C Programming Langage : http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocDefiningClasses.html#//apple_ref/doc/uid/TP30001163-CH12-TPXREF125
The instance variable is accessible within the class that declares it and within classes that inherit it. All instance variables without an explicit scope directive have @protected scope.
However, a public instance variable can be accessed anywhere as if it were a field in a C structure. For example:
Worker *ceo = [[Worker alloc] init];
ceo->boss = nil;
I have the compilation error using LLVM GCC 4.2 (for an iOS project, on device) :
error: 'fooInstanceVar' undeclared (first use in this function) and the same one using GCC 4.2 : error: 'fooInstanceVar' undeclared (first use in this function)
I can compile using LLVM Compiler 2.0 whithout error.
For compiling with LLVM GCC 4.2 and GCC 4.2 with the use of self-> :
[NSArray arrayWithObjects:self.barProperty, self->fooInstanceVar, nil]; in the doSomethingWithProperty method.
Related videos on Youtube
erikprice
Updated on June 04, 2022Comments
-
erikprice almost 2 years
Edit: I just noticed this other Stack Overflow question asking much the same thing: Why does a subclass @property with no corresponding ivar hide superclass ivars?
This is some interesting behavior that I cannot find documented in anything official or unofficial (blog, tweet, SO question, etc). I have boiled it down to its essence and tested this in a fresh Xcode project, but I can't explain it.
MyBaseClass has an instance variable:
@interface MyBaseClass : NSObject { NSObject *fooInstanceVar; } @end
MySubclass extends MyBaseClass, and declares a totally unrelated property (that is, the property is not intended to be backed by the instance variable):
#import "MyBaseClass.h" @interface MySubclass : MyBaseClass { } @property (nonatomic, retain) NSObject *barProperty; @end
If the implementation of MySubclass does not synthesize the property but implements the accessor methods, everything is fine (no compiler error):
#import "MySubclass.h" @implementation MySubclass - (NSObject*)barProperty { return [[NSObject alloc] init]; // pls ignore flagrant violation of memory rules. } - (void)setBarProperty:(NSObject *)obj { /* no-op */ } - (void)doSomethingWithProperty { NSArray *array = [NSArray arrayWithObjects:self.barProperty, fooInstanceVar, nil]; NSLog(@"%@", array); } @end
But if I remove the property accessor methods and replace them with a
synthesize
declaration for the property, I get a compiler error:'fooInstanceVar' undeclared (first use in this function)
.#import "MySubclass.h" @implementation MySubclass @synthesize barProperty; - (void)doSomethingWithProperty { NSArray *array = [NSArray arrayWithObjects:self.barProperty, fooInstanceVar, nil]; NSLog(@"%@", array); } @end
This error goes away if I remove either the
synthesize
declaration, or if I do not refer to thefooInstanceVar
instance variable from within MySubclass.m, or if I put all interface and implementation definitions in a single file. This error also seems to happen in both GCC 4.2 and GCC/LLVM build settings.Can anyone explain what's happening here?
-
albertamg over 12 yearsIt also works if you declare the
barProperty
backing ivar explicitly (vs automatic ivar) or if you try to access the superclass' ivar withself->fooInstanceVar
-
jscs over 12 yearsI have the same results as seppo0010. I also tried GCC 4.0 (via Xcode 3), which gave no error. GCC 4.2 certainly seems to be the culprit.
-
jscsYour question title and body don't match up -- the property and the ivar are not connected beyond having similar names.
-
seppo0010I was able to reproduce this issue with this code: gist.github.com/1123895 It seems to fail only using GCC 4.2, not with LLVM GCC 4.2 nor LLVM Compiler 2.0. The funniest thing is moving the @synthesize AFTER the method, it works. So, my conclusion is it is a compiler error.
-
albertamg
@synthesize objectInSubclassAsProperty = objectInBaseClass;
will result in a compilation error "attempted to use ivar declared in super class". @synthesize is only allowed to use an ivar from the current class, not a superclass.
-
-
jscs almost 13 yearsI don't think that's what erikprice is talking about -- the subclass property and the superclass ivar aren't connected in his code aside from having "objectIn" in their names.
-
jscs almost 13 yearsIn your defense, the title of the question does refer to the problem that you're describing.
-
erikprice almost 13 yearsYes, Josh is correct. I've tried to update the title and text of this question to be more clear. I am trying to add a property to a class (to achieve "non-fragile instance variables"), but this class refers to (other) instance variables that are declared in its superclass, and this seems to cause some kind of conflict during compilation. That conflict is what I am trying to understand.
-
erikprice almost 13 years"I just tried this and it compiles without warning. What am I not doing?" If I put both interfaces and implementations into a single file, I do not get the error. If I split them up into MyBaseClass.h/m and MySubclass.h/m, the compiler reports "'fooInstanceVar' undeclared (first use in this function)" in the body of -doSomethingWithProperty.
-
erikprice almost 13 years"It isn't clear what problem you are trying to solve. All instance variables are non-fragile everywhere but 32 bit Mac OS X." I shouldn't have brought up non-fragile instance vars, as I don't have a problem there. What I'm really trying to do is just add some synthesized properties to a subclass, that happens to have references to instance variables declared in its parent class, and getting a surprising compiler error – and just wanted to understand why my code is causing a problem. Nothing I can't work around, but I'd like to come away from this with a better understanding of Objective-C.
-
erikprice over 12 yearsYour SO link is broken. Other than that, it sounds like your answer is essentially an affirmation that you've experienced the problem I describe in my question too. That's heartening, sort of, but do you know why it happens? That's what I'm really after.
-
Dad over 12 years@bbum more specifically: /Users/Dad/Desktop/testObjC/Classes/MyBaseClass.m: In function '-[MySubclass doSomethingWithProperty]': /Users/Dad/Desktop/testObjC/Classes/MyBaseClass.m:18: error: 'fooInstanceVar' undeclared (first use in this function) /Users/Dad/Desktop/testObjC/Classes/MyBaseClass.m:18: error: (Each undeclared identifier is reported only once /Users/Dad/Desktop/testObjC/Classes/MyBaseClass.m:18: error: for each function it appears in.) Project and source here: dl.dropbox.com/u/1467270/testObjC.zip
-
Dad over 12 years@bbum I just pasted your code into an iOS project and got the OP error "/Users/Dad/Desktop/testObjC/Classes/MyBaseClass.m:18:0 /Users/Dad/Desktop/testObjC/Classes/MyBaseClass.m:18: error: 'fooInstanceVar' undeclared (first use in this function)" (Xcode 3.2.6: create new Utility Application project for iOS (default compiler is gcc4.2), Add new File "MyBaseClass.m" with "also create .h file" checked. Paste both class declarations into the .h file, and both implementations into the .m file, Build).
-
ıɾuǝʞ over 12 yearsFound an interesting issue : when compiling for iPhone, got the error, but no error when compiling for the simulator
-
ıɾuǝʞ over 12 yearsOptions for iphone :
llvm-gcc-4.2 -x objective-c -arch armv6 -fmessage-length=0 -pipe -std=gnu99 -Wno-trigraphs -fpascal-strings -O0 -Wreturn-type -Wunused-variable -Wshorten-64-to-32 -DDEBUG -isysroot iPhoneOS4.3.sdk -gdwarf-2 -mno-thumb -miphoneos-version-min=4.3 -iquote
-
ıɾuǝʞ over 12 yearsOptions for simulator : llvm-gcc-4.2 -x objective-c -arch i386 -fmessage-length=0 -pipe -std=gnu99 -Wno-trigraphs -fpascal-strings -O0 -Wreturn-type -Wunused-variable -Wshorten-64-to-32 -DDEBUG -isysroot iPhoneSimulator4.3.sdk -fexceptions -fasm-blocks -gdwarf-2 -fobjc-abi-version=2 -fobjc-legacy-dispatch -D__IPHONE_OS_VERSION_MIN_REQUIRED=40300