13

Update - Many people are insisting I need to declare an iVar for the property. Some are saying not so, as I am using Modern Runtime (64 bit). I can confirm that I have been successfully using @property without iVars for months now. Therefore, I think the 'correct' answer is an explanation as to why on 64bit I suddenly have to explicitly declare the iVar when (and only when) i'm going to access it from a child class. The only one I've seen so far is a possible GCC bug (thanks Yuji). Not so simple after all... To clarify the possible bug is this: When inheriting from a base class, a child can not access the parent's iVar IF that child also happens to implement an UNRELATED accessor using @synthesize BEFORE the iVar is accessed.

I've been scratching my head with this for a couple of hours - I haven't used inheritance much.

Here I have set up a simple Test B class that inherits from Test A, where an ivar is declared. But I get the compilation error that the variable is undeclared. This only happens when I add the property and synthesize declarations - works fine without them.

TestA Header:

#import <Cocoa/Cocoa.h>
@interface TestA : NSObject {
    NSString *testString;
}
@end

TestA Implementation is empty:

#import "TestA.h"
@implementation TestA  
@end

TestB Header:

#import <Cocoa/Cocoa.h>
#import "TestA.h"
@interface TestB : TestA {
}
@property (nonatomic, retain) NSString *testProp;
@end

TestB Implementation (Error - 'testString' is undeclared)

#import "TestB.h"
@implementation TestB
@synthesize testProp;
- (void)testing{
    NSLog(@"test ivar is %@", testString);
}
@end
jv42
  • 8,521
  • 5
  • 40
  • 64
Ben Packard
  • 26,102
  • 25
  • 102
  • 183
  • If it "only happens when [you] add the property and synthesize declarations", they're not a red herring. As I've mentioned, they're actually causing the problem because there is no `testProp` string instance variable. I declared one and the problem went away. – Quinn Taylor May 06 '10 at 06:15
  • I've just checked to make sure I wasn't going mental, and I use @property without an ivar all the time without any issues. I agree that declaring an ivar 'fixes' it, but doesn't really explain why it only fails to compile when I use inheritance. Just to re-iterate, I'm not declaring ivars for about 50 properties just within the same project, in many different classes, all without issue. – Ben Packard May 06 '10 at 06:22

4 Answers4

10

I think this is the bug of GCC 4.2.1. I made the file foo.m with the content

#import <Foundation/Foundation.h>
@interface TestA : NSObject {
    NSString *testString;
}
@end

@implementation TestA  
@end

@interface TestB : TestA {
}
@property (retain) NSString *testProp;
@end

@implementation TestB
@synthesize testProp;
- (void)testing{
NSLog(@"test ivar is %@", testString);
}
@end

Note that it's OK in the 64 bit mode to omit the instance variable. My GCC 4.2.1 on OS X 10.6.3 gave me an error:

$ gcc -arch x86_64 -c foo.m
aho.m: In function ‘-[TestB testing]’:
aho.m:19: error: ‘testString’ undeclared (first use in this function)
aho.m:19: error: (Each undeclared identifier is reported only once
aho.m:19: error: for each function it appears in.)

This compiled without problem by changing

NSLog(@"test ivar is %@", testString);

to

NSLog(@"test ivar is %@", self->testString);

Clang compiled it without any problem.

( In the 32 bit mode, I got

$ gcc -arch i386 -c foo.m
aho.m:17: error: synthesized property ‘testProp’ must either be named 
the same as a compatible ivar or must explicitly name an ivar
aho.m: In function ‘-[TestB testing]’:
aho.m:19: error: ‘testString’ undeclared (first use in this function)
aho.m:19: error: (Each undeclared identifier is reported only once
aho.m:19: error: for each function it appears in.)

which is a perfectly expected behavior, as Manjunath wrote.)

However I think it's generally a rather bad idea to access an instance variable of the superclass: when you implement the methods the superclass, you cannot assume anything about the instance variable because it might be tweaked in a worst manner possible by the subclass. You at least need to write down what kind of operation on the instance variable is permitted or not... Remember you might need to maintain your code for years! I would prefer keeping programming contracts between various parts of the code at the level of methods and properties.

Finally you should change

@property NSString *testProp;

to

@property (copy) NSString *testProp;

or at least to

@property (retain) NSString *testProp;

if you're not using GC on OS X. Otherwise EXP_BAD_ACCESS will await you!

Yuji
  • 34,103
  • 3
  • 70
  • 88
  • The first part is wrong; but for the second part: unless you /need/ it to support threaded access, (whatever, nonatomic) is also suggested. – Grant Paul May 06 '10 at 05:12
  • 1
    Naaaah! Did you both really compile the file? I combined everything in a `.m` file and gcc gave the error. I edit the post above to include the file I used. – Yuji May 06 '10 at 05:49
  • Manjunath - I am already importing the base class?! See code above. – Ben Packard May 06 '10 at 06:10
  • Yuji thanks for compiling this - surprised it could be a GCC error of all things! As for your point about accessing an ivar - the ivar in the actual project is an array. Say for example I have a super class Vehicle, that has an iVar array currentPassengers. Doesn't it make sense that all passenger carrying vehicles inherit this array? As well as inheriting methods like addPassenger and removePassenger, which could also be declared in the base class? Or how would you do it? – Ben Packard May 06 '10 at 06:44
  • Oh, and it DOES woek if I move the @synthesize to after the iVar reference - weird. – Ben Packard May 06 '10 at 07:15
  • In the case of an array like that, yes, I agree that it makes much sense to access it from the subclass; but I would put a comment in the `.h` and `.m` files exclaiming that *this ivar is accessed from subclass* :) – Yuji May 06 '10 at 07:19
  • Props to Yuji for trying Clang. When I compiled it, I hadn't considered that at first. I *was* the one who suggested moving the @synthesize after the ivar reference, though. That is incredibly odd that it works, to say the least. Did you include that in your bug report? – Quinn Taylor May 06 '10 at 13:37
  • I guess GCC mistakenly clears the internal ivar table(?) when it adds the auto-generated ivar for a property... The source code of GCC is available but I've never tried to read it, it's massive! – Yuji May 06 '10 at 15:12
1

I think you just have a typo - it should be "testString" not "test"

Andy White
  • 86,444
  • 48
  • 176
  • 211
1

I'm seeing error: 'testString' undeclared (first use in this function) when the @synthesize is just before the testing method. The error disappears if I move the @synthesize below the method implementation. This might be because the TestB class doesn't have a testProp string instance variable to use with the declared property. (In the Legacy (32-bit) runtime, you must declare instance variables to use for properties — in Modern runtime (64-bit Mac, iPhone) they can be inferred, so declaring them is optional.) Is it possible that you meant to name the property testString instead?


EDIT: In GCC 4.2, it works if you change TestB.h to the following:

#import "TestA.h"

@interface TestB : TestA {
    NSString *testProp; // <-- Adding this fixes the errors
}
@property NSString *testProp;

@end

However, using the Clang-LLVM compiler, the code works unmodified. Perhaps this is a bug to file.

Quinn Taylor
  • 44,553
  • 16
  • 113
  • 131
  • Yes that was a copy and paste mistake, corrected. But i've been using properties for 6 months without declaring ivars?!? eg. @property (nonatomic, retain) – Ben Packard May 06 '10 at 06:05
  • Or does the retain do that? Either way, doesn't resolve it unfortunately. – Ben Packard May 06 '10 at 06:07
  • You don't have to declare instance variables for the properties if you're using modern runtime. Modern runtime includes iPhone OS and 64 bit on OS X. – Yuji May 06 '10 at 06:10
  • I'm not sure what copy/paste error you mean — it's still not correct. Your property is called "testProp", your string ivar is called "testString". Unless you rename one of them or also declare `NSString* testProp` in TestA or TestB, you'll have problems. Also, I'm not understanding what you mean by not declaring ivars with the example you provide — that's a property declaration, and ivars (instance variables) are declared within the curly braces of the `@interface ... @end` code. – Quinn Taylor May 06 '10 at 06:11
  • 1
    Exactly - I'm saying that for 6 months I've been declaring string properties for example (with nonatomic, retain) without having to declare an ivar in the curly braces. – Ben Packard May 06 '10 at 06:14
  • Well, `@property (nonatomic, retain)` by itself is not correct without the name of the property afterward, which is why I was confused. If you've been successfully declaring properties without a matching ivar in curly braces, that means you're using Modern runtime (iPhone or 64-bit Mac). However, if the error goes away when you omit the property, I find it a hard sell to say that's not related to the problem. Even if I switch to 64-bit Intel only, I still get errors without the `testProp` ivar. Odd. – Quinn Taylor May 06 '10 at 06:25
  • @KennyTM I mention in my answer that declaring property ivars is optional in Modern runtime. For whatever reason, adding the ivar fixes the issue. It's possible that this is a bug, in GCC, as Clang-LLVM compiles the code as-is without issue... – Quinn Taylor May 06 '10 at 06:31
  • 1
    Yep that's my original problem - why do I need the iVar only when inheriting. – Ben Packard May 06 '10 at 06:32
  • You don't have to. This is definitely a bug in the GCC. Ben should report it to Apple! – Yuji May 06 '10 at 06:37
  • Oh my, that's my second bug report! - I only picked up Hillegass' Cocoa book a few months ago and now look! Thanks Yuji! – Ben Packard May 06 '10 at 06:53
  • If you reported a bug to Apple, please include the Radar number here for reference, and consider posting it on http://openradar.appspot.com/ so the community can track it as well. – Quinn Taylor May 06 '10 at 13:32
  • Where would you guys recommend I go to post the bug? Expecting one of two obvious answers, but I don't know which one! – Ben Packard May 09 '10 at 08:23
  • Go to bugreport.apple.com first, then openradar.appspot.com and post the contents of your Radar (Apple bug report). This is a bug that I'd think the community would appreciate being aware of. :-) – Quinn Taylor May 10 '10 at 18:18
1

I just ran into that same problem but the sources were complex enough that I didn't quite understand what made iVar from the parent inaccessible when I used GCC. I only knew for sure that a few months ago and before changes in my code it worked and also that it is working with clang which I've been using for a while. Suddenly I had to build with GCC and it wouldn't anymore.

At least this article gave me a bypass (declare the iVars). I am surprised that the latest version of XCode does not include a fixed compiler

terrinecold
  • 231
  • 1
  • 9