Protected Methods
Base.h
1 2 3 4 5 6 7 8 9 10 11 |
@interface Base : NSObject { } //declaring public member variables @property(nonatomic, assign) int publicAge; @property(nonatomic, strong) NSString* publicName; -(void)publicMethod ; //declaring a public method @end |
Note, that private attributes are accessible only by the class that declared it. No others can access it. Hence, in our Base class, it declares private attributes and freely uses it. However, in main, only the public attributes appears and can be accessed.
Base.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#import "Base.h" @interface Base () { } //declare private variable @property(nonatomic) long privateSSN; -(void)privateMethod; //declare a private method @end @implementation Base //auto-synthesized -(instancetype)init { if(self=[super init]) { //using public self.publicAge = 34; self.publicName = @"ricky"; //using private self.privateSSN = 553972870; [self privateMethod]; return self; } return nil; } -(void)publicMethod { NSLog(@"%@ - calling public method", [self class]); } -(void)privateMethod { NSLog(@"%@ - calling private method", [self class]); } @end |
Public means anyone can access these variables or methods. This includes the object themselves, as well as other objects.
When you create the Base object, and try to access the public attributes, you will see all the public members and methods we’ve declared.
Private access from other objects
You will not be able to see the private members and methods because you are not allowed access to them. Only the Base class that declared those private attributes can access them.
main.m
1 2 3 4 5 6 7 |
Base * person = [[Base alloc] init]; person.publicAge = 24; person.publicName = @"hadoken"; [person publicMethod]; NSLog(@"done"); |
Derived Classes
Say we derive a child class from Base.
1 2 3 4 5 6 7 |
#import "Base.h" @interface Child1 : Base { } @end |
Child1.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#import "Child1.h" @interface Child1 () @end @implementation Child1 -(instancetype)init { if(self=[super init]) { //using parent's public varaible self.publicAge = 34; self.publicName = @"ricky"; //using parent's public method [self publicMethod]; //using private self.privateSSN = 553972870; //ERROR [self privateMethod]; //ERROR return self; } return nil; } @end |
When you create a child class and derive it from a base class, you can only use the parent class’ public attributes. You cannot use its private attributes.
In other languages, we’d usually protected. Which is basically private + the ability to let child classes access your private attributes.
Protected Members (iVars)
Base.h
1 2 3 4 5 6 |
@interface Base : NSObject { @protected NSString * protectedString; } @end |
Base.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#import "Base.h" @interface Base () { } @end @implementation Base -(instancetype)init { if(self=[super init]) { NSLog(@"----- BASE INIT start------ "); NSLog(@"Base.m - iVar protectedString set"); protectedString = @"Base set protectedString"; NSLog(@"----- BASE INIT end ------ "); return self; } return nil; } @end |
Child.h
1 2 3 4 5 6 |
#import "Base.h" @interface Child : Base { } -(void)logProtectedString; @end |
Child.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#import "Child.h" @implementation Child -(instancetype)init { if(self=[super init]) { NSLog(@"----- CHILD INIT start ------ "); NSLog(@"Child: Uses iVar protectedString"); protectedString = @"This child likes to laugh.. haha!"; NSLog(@"----- CHILD INIT end ------ "); return self; } return nil; } -(void)logProtectedString { NSLog(@"Child.m - %@", protectedString); } @end |
Main
1 2 3 4 5 6 7 8 9 10 11 12 |
#import "Child.h" int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... NSLog(@"Hello, World!"); Child * aKid = [[Child alloc] init]; [aKid logProtectedString]; } return 0; } |
So basically, Child derives from Base. It access Base’s protected string, changes its content, and logs it. Thus we see
Child.m – This child likes to laugh.. haha!
Which is what we set in the Child class. This is how we use protected variables. However, if we were to use properties, it gets a little bit complicated.
Protected Variables (property)
First, we add properties get/set to our protectedString by doing:
1 2 3 4 5 6 7 8 |
@interface Base : NSObject { @protected NSString * protectedString; } @property(nonatomic, strong) NSString * protectedString; @end |
Then synthesize it to generate auto-get/set methods
1 2 3 4 5 6 7 8 9 |
@interface Base () { } @end @implementation Base @synthesize protectedString; ... |
Finally, we implement a custom set method so that we can see how it all works. Put the custom method in your Base.m file:
Base.m
1 2 3 4 5 6 7 |
-(void)setProtectedString:(NSString *)newStr { NSLog(@"Base.m [Calling class %@] - setProtectedString <--", [self class]); NSLog(@"Base.m - protectedString was: %@", protectedString); protectedString = newStr; NSLog(@"Base.m [Calling class %@] - setProtectedString -->", [self class]); } |
Be careful you don’t use self.protectedString because by definition, you’ll go into an infinite loop: You keep accessing your own set method.
In both Child and Base’s init, do self.protectedString. That way, we use and make our properties take into effect.
output:
—– BASE INIT start——
Base.m – iVar protectedString set
Base.m [Calling class Child] – setProtectedString <-- Base.m - protectedString was: (null) Base.m [Calling class Child] - setProtectedString -->
—– BASE INIT end ——
—– CHILD INIT start ——
Uses iVar protectedString
Base.m [Calling class Child] – setProtectedString <-- Base.m - protectedString was: Base set protectedString Base.m [Calling class Child] - setProtectedString -->
—– CHILD INIT end ——
Child.m – This child likes to laugh.. haha!
You will see that Child object in main does its init, which uses Base class’s init. Base class’s init simply uses the self.protectedString property, which calls the custom set method setProtectedString.
The custom set method sees that the iVar protectedString was null, then it gets set to “Base set protectedString”.
The Child’s init runs and using the property self.protectedString. It sees that it as a custom setProtectedString method in its Parent class, and uses that to set the iVar protectedString to a new value.
Over-riding Propert’s custom method
If your Child class wants to use its own custom method, then over-ride the Base class’s custom set method setProtectedString.
Child.m
1 2 3 4 5 6 7 |
-(void)setProtectedString:(NSString *)newStr { NSLog(@"Child.m [Calling class %@] - setProtectedString <--", [self class]); NSLog(@"Child.m - protectedString was: %@", protectedString); protectedString = newStr; NSLog(@"Child.m [Calling class %@] - setProtectedString -->", [self class]); } |
output:
Hello, World!
—– BASE INIT start——
Base.m – iVar protectedString set
Child.m [Calling class Child] – setProtectedString <--
Child.m - protectedString was: (null)
Child.m [Calling class Child] - setProtectedString -->
—– BASE INIT end ——
—– CHILD INIT start ——
Child: Uses iVar protectedString
Child.m [Calling class Child] – setProtectedString <-- Child.m - protectedString was: Base set protectedString Child.m [Calling class Child] - setProtectedString -->
—– CHILD INIT end ——
Child.m – This child likes to laugh.. haha!
Take heed that in Base’s init method, the self.protectedString accesses the custom set method in Child as shown in the output. This is because Child over-rode its parent class’s setProtectedString method.
The attribute protectedString still belongs to the Base class. The property generated its get/set in the Base class. But the Child class over-rode the set method, and thus, that’s why in Base’s init, we access the set method in Child.
Protected Methods
we use category in a dedicated header file
Base+Protected.h
1 2 3 4 5 6 7 |
#import <Foundation/Foundation.h> @interface Base(Protected) - (void)protectedMethod; @end |
Then, import the Base+Protected.h category file into your Base class. Make sure you implement the protected method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#import "Base.h" #import "Base+Protected.h" @interface Base () { } @end @implementation Base @synthesize protectedString; - (void)protectedMethod { NSLog(@"%@ -protectedMethod", [self class]); } @end |
Note that IF we have not imported Base+Protected.h, “protectedMethod” method would be a private method.
Before, we can access parent’s public. If you try to access Base’s protectedMethod, it will see it as trying to access the private of the Base class, and throw an error:
1 2 3 4 5 6 7 8 |
if(self=[super init]) { //using parent's public method [self publicMethod]; [self protectedMethod]; //ERROR! return self; } |
You need to #import “Base+Protected.h”, then you will be able to access the protected method which was attached to the Parent class via Category.
Thus, you just created protected access in objective c
1 2 3 4 5 6 7 8 9 10 11 |
#import "Base+Protected.h" ..... .... if(self=[super init]) { //using parent's public method [self publicMethod]; [self protectedMethod]; //OK! return self; } |
The import of the category .h file acts as an access to private attributes mechanism. In obj c, we simply use it to give us protected-access behavior.
When you run it, you will see that the Child class calls the Base class.
Without a protected category, there would be no way for subclasses to access this method.
Keep in mind that protected attributes are really meant for child classes to use. It is still private in nature, and should not let global objects access them. They should only be accessed by its parent and anyone who is their children.
Keep in mind that accessing private and over-riding are two different things
In this section, we talk about how to have child objects access parent’s private methods via protected mechanism. However, child objects CAN accidently OVER-RIDE parent private methods: