objective-csubclasssuperclassivar

Access ivar from subclass in Objective-C


I have class A which has this declaration in it's .m file:

@implementation A {
    NSObject *trickyObject;
}

And class B which has this declaration in it's .h file:

@interface B : A
@end

Is there any possibility to access the trickyObject from a method declared in the class B?


Solution

  • If you have a property or method that is private, but you want to make accessible to subclasses, you can put the declaration in a category.

    So consider A:

    //  A.h
    
    @import Foundation;
    
    @interface A : NSObject
    
    // no properties exposed
    
    @end
    

    And

    //  A.m
    
    #import "A.h"
    
    // private extension to synthesize this property
    
    @interface A ()
    
    @property (nonatomic) NSInteger hiddenValue;
    
    @end
    
    // the implementation might initialize this property
    
    @implementation A
    
    - (id)init {
        self = [super init];
        if (self) {
            _hiddenValue = 42;
        }
        return self;
    }
    
    @end
    

    Then consider this category:

    //  A+Protected.h
    
    @interface A (Protected)
    
    @property (readonly, nonatomic) NSInteger hiddenValue;
    
    @end
    

    Note, this extension doesn’t synthesize the hiddenValue (the private extension in A does that). But this provides a mechanism for anyone who imports A+Protected.h to have access to this property. Now, in this example, while hiddenValue is really readwrite (as defined in the private extension within A), this category is exposing only the getter. (You obviously could omit readonly if you wanted it to expose both the getter and the setter, but I use this for illustrative purposes.)

    Anyway, B can now do things like:

    //  B.h
    
    #import "A.h"
    
    @interface B : A
    
    - (void)experiment;
    
    // but again, no properties exposed
    
    @end
    

    And

    //  B.m
    
    #import "B.h"
    #import "A+Protected.h"
    
    @implementation B
    
    // but with this category, B now has read access to this `hiddenValue`
    
    - (void)experiment {
        NSLog(@"%ld", (long)self.hiddenValue);
    }
    
    @end
    

    Now A isn’t exposing hiddenValue, but any code that uses this A (Protected) category (in this case, just B) can now access this property.

    And so now you can call B methods that might be using the hiddenValue from A, while never exposing it in the public interfaces.

    //  ViewController.m
    
    #import "ViewController.h"
    #import "B.h"
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        B *b = [[B alloc] init];
        [b experiment];          // this calls `B`’s exposed method, and that method is using the property not exposed by `A.h`
    }
    
    @end
    

    If you’re interested in a real-world example of this, consider UIKit’s:

    @import UIKit.UIGestureRecognizerSubclass;
    

    Generally the state of a UIGestureRecognizer is readonly, but this UIGestureRecognizer (UIGestureRecognizerProtected) category exposes the readwrite accessors for state (to be used, as the name suggests, by gesture recognizer subclasses only).