26

In the superclass MyClass:

@interface MyClass : NSObject

@property (nonatomic, strong, readonly) NSString *pString;

@end

@implementation MyClass

@synthesize pString = _pString;

@end

In the subclass MySubclass

@interface MySubclass : MyClass

@end

@implementation MySubclass

- (id)init {
    if (self = [super init]) {
        _pString = @"Some string";
    }
    return self;
}

The problem is that the compiler doesn't think that _pString is a member of MySubclass, but I have no problem accessing it in MyClass.

What am I missing?

tacos_tacos_tacos
  • 10,277
  • 11
  • 73
  • 126

3 Answers3

54

The instance variable _pString produced by @synthesize is private to MyClass. You need to make it protected in order for MySubclass to be able to access it.

Add an ivar declaration for _pString in the @protected section of MyClass, like this:

@interface MyClass : NSObject {
    @protected
    NSString *_pString;
}

@property (nonatomic, strong, readonly) NSString *pString;

@end

Now synthesize the accessors as usual, and your variable will become accessible to your subclass.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Yes, you will...if it is from a subclass that has access to the underlying variable. – borrrden Jun 08 '12 at 04:36
  • 2
    @Owl You will not be able to set the string through the property, but you will be able to set the string to the backing variable, which will have the effect of `pString` property changing its value. You need to set `_pString = @"abc"`, not `self.pString = @"abc"`. – Sergey Kalinichenko Jun 08 '12 at 04:38
  • @Owl what dasblinken said! You don't HAVE to set the variable through its property interface. This way objects that don't inherit from the class cannot change the variable. It is readonly except to children. – borrrden Jun 08 '12 at 04:39
  • Just a semantic question about stack...does "oldest" answer go by creation time or edit time? – borrrden Jun 08 '12 at 04:42
  • 1
    Good answer. I would personally use a double under score to indicate that you are accessing a protected ivar on a superclass and explicitly @synthesize it: – SmileBot Mar 29 '14 at 18:40
  • 1
    A small reminder: in the subclass use the ivar (_myVariable) instead of the accessor (self.myVariable). – Rudolf Real Jan 17 '17 at 15:20
5

I am familiar with this problem. You synthesize the variable in your .m class, so it is not imported along with the header since the _pString variable will be created as part of the implementation, and not the interface. The solution is to declare _pString in your header interface and then synthesize it anyway (it will use the existing variable instead of creating a private one).

@interface MyClass : NSObject
{
    NSString *_pString; //Don't worry, it will not be public
}

@property (nonatomic, strong, readonly) NSString *pString;

@end
borrrden
  • 33,256
  • 8
  • 74
  • 109
0

The given answer works perfectly fine. This is an alternative answer, that apparently Apple likes a bit more.

You can define a private extension of your class, a MyClass+Protected.h file, which needs to be included in MyClass.m and MySubclass.m.

Then, in this new file, you redefine the property as readwrite.

@interface MyClass ()
@property (strong, readwrite) NSString * pString;
@end

This alternative allows you to use the accessor self.pString rather than the ivar _pString.

Note: you still need to keep the definition of pString in your MyClass.h as is.

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
Rudolf Real
  • 1,948
  • 23
  • 27