5

When I just declare a @property in a superclass without declaring ivars, subclass it and try to implement getter using the superclasses ivar (_propertyName) in subclass, xcode invokes an error stating Use of undeclared identifier '_propertyName'.

What is the solution conforming to best programming practices?

Should I @synthesize propertyName = _propertyName in the @implementation of the subclass or

@interface SuperClass : AnotherClass
{
    Type *_propertyName;
}

@property Type *propertyName;

@end

EDIT:
I do understand the automatic "synthesis" of the properties' accessor methods and creation of "underbar ivars" by the compiler.
The ivar is accessible from the implementation of the SuperClass without any @synthesize or declaration of ivars in the interface or implementation section.

Further clarification of my case: Disclaimer: Contents stolen block of code from Alfie Hanssen

@interface SuperViewController : UIViewController
@property (nonatomic, strong) UITableView * tableView; // ivar _tableView is automatically @synthesized
@end

#import "SuperViewController.h"

@interface SubViewController : SuperViewController
// Empty
@end

@implementation SubViewController

- (void)viewDidLoad
{
    NSLog(@"tableView: %@", self.tableView); // this is perfectly OK
}

// ************* This causes problem **************
- (UITableView *) tableView {
    if (!_tableView) {    // Xcode error: Use of undeclared identifier '_propertyName'
        _tableView = [[SubclassOfUITableView alloc] init];
    }
    return _tableView;
}
// ************************************************


@end
user2626382
  • 85
  • 2
  • 7
  • This is strange: in Objective C ivars are protected by default, so your subclasses should be able to access them as if they were their own. How do you access `_propertyName` to get that error? – Sergey Kalinichenko Aug 06 '13 at 14:47
  • 4
    If your property is `@property Type *_propertyName;` then I would think the ivar would be `__propertyName`. (Two underscores) Why does your subclass need direct access to the ivar? – BergQuester Aug 06 '13 at 14:49
  • FYI, there's no longer any need to `@synthesize` properties; just declare them in the header file and the synthesis is done automatically by Xcode/the compiler. – davidf2281 Aug 06 '13 at 14:49
  • Sorry, I corrected the typo. The ivar is created automatically but it's only accessible by the superclass directly. – user2626382 Aug 06 '13 at 14:53
  • @BergQuester I am using the ivar in the SuperClass to store an object that in the subclass's implementation is a subclass of the object. – user2626382 Aug 06 '13 at 14:56
  • See the first answer at http://stackoverflow.com/questions/822487/how-does-an-underscore-in-front-of-a-variable-in-a-cocoa-objective-c-class-work. – Fred Aug 06 '13 at 14:58
  • @Fred Xcode from some version creates the underscore instance variables for you unless you implement all the allowed setter/getter methods (meaning if the property is `readonly`, it is sufficient to implement the getter in order not to get the ivar autosynthesized) - in my case, I only implement the getter (lazy instantiation) and the property is readwrite - so I don't have to synthesize the property methods and assign it the underbar ivar name. I am only looking for the answer to this specific situation, when I use the ivar in the subclass directly. – user2626382 Aug 06 '13 at 15:25
  • @JoshCaswell Yes, that is what I was looking for. A straightforward statement. I have not found it mentioned explicitly anywhere on the internet. – user2626382 Aug 06 '13 at 20:19
  • @dasblinkenlight: Synthesized ivars are [more private than private](http://stackoverflow.com/q/8510464). – jscs Aug 06 '13 at 20:21
  • Well then, you've come to the right place, @user2626382! – jscs Aug 06 '13 at 20:22
  • @JoshCaswell Thanks! That covers it all up. But now I don't know how to mark your answer as accepted since it's just a comment. – user2626382 Aug 06 '13 at 21:36
  • The ideal move would be marking the question as a duplicate so that the information can be consolidated in one place -- a moderator can do this if you raise a flag. Anyways, glad you found my answer helpful. – jscs Aug 06 '13 at 22:52

5 Answers5

7

If you want to use an ivar in both the superclass and the subclass, you have to declare it in the interface of the superclass, because it's the only file that's included in both implementations. Otherwise, you're just trying to guess what could be in the implementation of the superclass and xcode will not play game.

The above is true whether there is a property that use that ivar or not.

Now if you have a property in the superclass and you write in the subclass implementation :

@synthesize propertyName = _propertyName

You're just saying that you're ditching whatever implementation of that property was in the superclass and you want the standard setter and getter generated by xcode, working on an ivar named _propertyname. Maybe the superclass works the same way, maybe not.

Nicolas
  • 923
  • 7
  • 11
0

This should work just fine. self.tableView and _tableView should both be accessible from SubViewController.

@interface SuperViewController : UIViewController
@property (nonatomic, strong) UITableView * tableView; // ivar _tableView is automatically @synthesized
@end

#import "SuperViewController.h"

@interface SubViewController : SuperViewController
// Empty
@end

@implementation SubViewController

- (void)viewDidLoad
{
    NSLog(@"tableView: %@", self.tableView);
}

@end

Edit: Okay now I understand you're question better. Check out this question. Seems like a duplicate and seems like it answers your question. I.e. looks like you'll have to explicitly define the instance variable.

Community
  • 1
  • 1
Alfie Hanssen
  • 16,964
  • 12
  • 68
  • 74
  • Okay, what if I added this method to the SubViewController implementation: - (UITableView *) tableView { if (!_tableView) { _tableView = [[SubclassOfUITableView alloc] init]; } return _tableView; } – user2626382 Aug 06 '13 at 15:08
  • “ivar _tableView is automatically \@synthesized” How do you know that ? You're assuming that there is no \@synthesize in the m file nor custom implementation of the getter or setter. How can you know that ? – Nicolas Aug 06 '13 at 15:19
  • It is not a duplicate of that question since the cases are opposite. I want the ivar, I don't have to `synthesize` it, since the superclass only implements setter and the property is `readwrite` - the ivar is easily accessible from the implementation of the superclass. – user2626382 Aug 06 '13 at 15:37
0

Apparently the automatic synthesis of the ivar doesn't work in this case. The following code fails with the compiler error "use of undeclared identifier _x", but only for the use in JFNBclass. Uncomment the ivar declaration, and the compiler error goes away. Default synthesis doesn't seem to create _x that is visible to the inheriting class.

JFNAclass.h

#import <Foundation/Foundation.h>

@interface JFNAclass : NSObject {
  // NSNumber *_x;
}

@property NSNumber *x;
- (NSNumber *) xTest;

@end

JFNAclass.m

#import "JFNAclass.h"

@implementation JFNAclass

- (NSNumber *) xTest
{
  return _x;
}

@end

JFNBclass.h

#import <Foundation/Foundation.h>
#import "JFNAclass.h"

@interface JFNBclass : JFNAclass

- (void) printIt;
- (void) setIt;

@end

JFNBclass.m

#import "JFNBclass.h"

@implementation JFNBclass

- (void) setIt
{
    _x = [[NSNumber alloc] initWithInt:2];
}

- (void) printIt
{
  NSLog(@"_x is %@", _x);
}

@end
Fred
  • 8,582
  • 1
  • 21
  • 27
  • 1
    [What is the visibility of synthesized instance variables?](http://stackoverflow.com/q/8510464) – jscs Aug 06 '13 at 19:45
  • I didn't run that test... ... the results are completely consistent with the SO answer you referenced. Quelle surprise! So I upvoted that answer. – Fred Aug 06 '13 at 20:22
  • It's nice of you that you took the time to write this down, I had almost identical code wrote down. but if you read carefully, I even stated this solution in the title. – user2626382 Aug 06 '13 at 21:21
  • Sorry, the question mark in your title led me to infer you had a question about which technique would work. – Fred Aug 06 '13 at 23:43
0

Ivars created by @synthesize are @private. (This is true for both explicit @synthesize statements and automatic synthesis.)

Your class SuperViewController does have an ivar _tableView, but it is @private so your subclass can't use it. If you need an ivar that subclasses can use, you'll need to declare that ivar explicitly and make it something other than @private. (You'd also want to use @synthesize tableView=_tableView so the property uses your declared ivar.)

Greg Parker
  • 7,972
  • 2
  • 24
  • 21
0

I wouldn't access ivar at all. You want to override getter, so just call super.

- (UITableView *)tableView {
    UITableView *tableView = [super tableView];
    if ( ! tableView) {
        tableView = [[SubclassOfUITableView alloc] init];
        self.tableView = tableView;
    }
    return tableView;
}
Tricertops
  • 8,492
  • 1
  • 39
  • 41
  • I should have probably provided more code. Analogously to my case `[super tableView]` would return `(UITableView *)` (`alloc`ed and `init`ed) - not `nil` - so the subclass's getter would (probably)never return `SubclassOfUITableView`. – user2626382 Aug 06 '13 at 21:51
  • Still, **I wouldn't access ivar at all.** If both your getters are lazy, but they return different class of `UITableView`, then create method `- (Class)tableViewClass` are override it. There is always nice solution. – Tricertops Aug 07 '13 at 06:10