If a subclass refers to a superclass ivar, synthesizing an unrelated property fails

11,060

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.

Share:
11,060

Related videos on Youtube

erikprice
Author by

erikprice

Updated on June 04, 2022

Comments

  • erikprice
    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 the fooInstanceVar 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
      albertamg over 12 years
      It also works if you declare the barProperty backing ivar explicitly (vs automatic ivar) or if you try to access the superclass' ivar with self->fooInstanceVar
    • jscs
      jscs over 12 years
      I 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.
    • jscs
      jscs
      Your question title and body don't match up -- the property and the ivar are not connected beyond having similar names.
    • seppo0010
      seppo0010
      I 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
      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
    jscs almost 13 years
    I 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
    jscs almost 13 years
    In your defense, the title of the question does refer to the problem that you're describing.
  • erikprice
    erikprice almost 13 years
    Yes, 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
    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
    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
    erikprice over 12 years
    Your 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
    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
    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ǝʞ
    ıɾuǝʞ over 12 years
    Found an interesting issue : when compiling for iPhone, got the error, but no error when compiling for the simulator
  • ıɾuǝʞ
    ıɾuǝʞ over 12 years
    Options 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ǝʞ
    ıɾuǝʞ over 12 years
    Options 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