Search code examples
objective-cxcode4.5protected

How to simulate protected properties and methods in objective-c


Possible Duplicate:
Protected methods in objective-c

The way to declare private properties is simple.

You declare that in extension that's declared in .m files.

Say I want to declare protected properties and access it from the class and subclass.

This is what I tried:

//
//  BGGoogleMap+protected.h
//
//

#import "BGGoogleMap.h"

@interface BGGoogleMap ()
@property (strong,nonatomic) NSString * protectedHello;
@end

That one is compile. Then I added:

#import "BGGoogleMap+protected.h"

@implementation BGGoogleMap ()

-(NSString *) protectedHello
{
    return _
}

@end

Problem starts. I can't implement class extension outside the original .m files it seems. Xcode will demand something inside that bracket.

If I do

#import "BGGoogleMap+protected.h"

@implementation BGGoogleMap (protected)

-(NSString *) protectedHello
{
    return _
}

@end

I cannot access the ivar of _protectedHello declared in BGGoogleMap+protected.h

Of course I can use regular category rather than extension, but that means I can't have protected properties.

So what should I do?


Solution

  • The Objective-C Programming Language says this:

    Class extensions are like anonymous categories, except that the methods they declare must be implemented in the main @implementation block for the corresponding class.

    So you could just implement your class extension's methods in the class's main @implementation. That is the simplest solution.

    A more complicated solution is to declare your “protected” messages and properties in a category, and declare any instance variables for that category in a class extension. Here's the category:

    BGGoogleMap+protected.h

    #import "BGGoogleMap.h"
    
    @interface BGGoogleMap (protected)
    
    @property (nonatomic) NSString * protectedHello;
    
    @end
    

    Since a category cannot add an instance variable to hold protectedHello, we need a class extension also:

    `BGGoogleMap_protectedInstanceVariables.h'

    #import "BGGoogleMap.h"
    
    @interface BGGoogleMap () {
        NSString *_protectedHello;
    }
    @end
    

    We need to include the class extension in the main @implementation file so that the compiler will emit the instance variable in the .o file:

    BGGoogleMap.m

    #import "BGGoogleMap.h"
    #import "BGGoogleMap_protectedInstanceVariables.h"
    
    @implementation BGGoogleMap
    
    ...
    

    And we need to include the class extension in the category @implementation file so that the category methods can access the instance variables. Since we declared the protectedHello property in a category, the compiler will not synthesize the setter and getter method. We have to write them by hand:

    BGGoogleMap+protected.m

    #import "BGGoogleMap+protected.h"
    
    @implementation BGGoogleMap (protected)
    
    - (void)setProtectedHello:(NSString *)newValue {
        _protectedHello = newValue; // assuming ARC
    }
    
    - (NSString *)protectedHello {
        return _protectedHello;
    }
    
    @end
    

    Subclasses should import BGGoogleMap+protected.h to be able to use the protectedHello property. They should not import BGGoogleMap_protectedInstanceVariables.h because the instance variables should be treated as private to the base class. If you ship a static library without source code, and you want users of the library to be able to subclass BGGoogleMap, ship the BGGoogleMap.h and BGGoogleMap+protected.h headers, but don't ship the BGGoogleMap_protectedInstanceVariables.h header.