Simulating protected properties and selectors in Objective-C
There’s no protected
modifier in Objective-C. In a sense, we should be
grateful that Objective-C is not Java. On the other hand, sometimes it can suck.
What’s the best way to deal with it? Let’s simulate protection!
The problem is already asked and answered
When looking for a solution to this, I found these two StackOverflow posts:
which exposed the real case where the lack of protected
sucks (ie subclassing)
and also the solution to the problem (ie simulating protection) as used by Apple
herself in classes such as
UIGestureRecognizer
.
A Concrete Example
The public…
Let’s say I have a BSDaddy
base class with only one publicly declared taskk:
@interface BSDaddy : NSObject
- (NSString *)introduction;
@end
Now I want a BSSon
class, inheriting from BSDaddy
.
#import "BSDaddy.h"
@interface BSSon : BSDaddy
@end
… the protected…
Let’s say, Dad has information it should share with his son, like an address for
instance. Let’s create a new file BSDaddySubclass.h
in which we declared this
kind of stuff.
@interface BSDaddy ()
@property(nonatomic, strong) NSString *protectedAddress;
- (NSString *)healthInsuranceId;
@end
Please note that I’m using ()
instead of something more precise like
(subclass)
as, otherwise, the properties are not auto-synthetized.
… and the private
Here is a basic implementation of Dad, in which we also defined stuff we want to keep private from Son.
#import "BSDaddy.h"
#import "BSDaddySubclass.h"
@interface BSDaddy ()
@property(weak, nonatomic) NSString *secretSexFantasy;
- (NSNumber *)creditCardPinNumber;
@end
@implementation BSDaddy
- (id)init {
self = [super init];
if (self) {
self.protectedAddress = @"Hill Valley";
self.secretSexFantasy = @"being disguised as an alien";
}
return self;
}
- (NSString *)introduction { return @"Hi! My name is George McFly!"; }
- (NSString *)healthInsuranceId { return @"123456789"; }
- (NSNumber *)creditCardPinNumber { return [NSNumber numberWithInteger:1234]; }
Please note that we reuse ()
for the new interface declaration, it will not
conflict with the one in BSDaddySubclass.h
Now let’s implement Son
Son is a rebel and want to share everything about his dad when he introduces himself.
#import "BSSon.h"
#import "BSDaddySubclass.h"
@implementation BSSon
- (NSString *)introduction {
// Property 'secretSexFantasy' not found on object of type 'BSSon *'
// NSString *myDadSecretFantasy = self.secretSexFantasy;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
NSString *myDadSecretFantasy = nil;
NSNumber *creditCardPinNumber = nil;
if ([self respondsToSelector:@selector(secretSexFantasy)]) {
myDadSecretFantasy = [self performSelector:@selector(secretSexFantasy)
withObject:nil];
}
if ([self respondsToSelector:@selector(creditCardPinNumber)]) {
creditCardPinNumber = [self performSelector:@selector(creditCardPinNumber)
withObject:nil];
}
#pragma clang diagnostic pop
return [NSString stringWithFormat:@"Hi! I'm Marty. My dad says: “%@”. He'll be mad cause I'm making public that we live in %@ and his health insurance ID is %@. He'll be even madder after I tell you his secret sex fantasy is %@, and that his credit card PIN number is %@.", [super introduction], self.protectedAddress, [self healthInsuranceId], myDadSecretFantasy, creditCardPinNumber];
}
Please note:
BSDaddySubclass.h
is imported.- The properties and tasks declared in
BSDad.m
can’t be used: you will get a compilation error if you try - The properties and tasks declared in
BSDadSubclass.h
can be used normally without the need of an extra@synthesize
or@dynamic
for properties. - Most importantly, please note that this is just simulated protection, which
relies on a convention! If another class decides to import
BSDadSubclass.h
, nothing will prevent it to do so and use the protected methods as public ones. You just need to establish the convention that this is bad programming within your team. To make this even clearer, remember as well that there is no such things as private methods either. If you know a private method exists, you can still call it. That’s the power of Objective-C and remember what Uncle Ben said: “With great power comes great responsability”! - Optional: notice also the several
#pragma
that will prevent you to get warnings when trying to perform private selectors in your code. If you need them, you should be aware maybe you’re doing something nasty.
Conclusion
This is how George and Marty will introduce themselves:
You can find the full source code of this example on my iOS Demo code on GitHub.
And again, if you want to address me some feedback or comments, it’s via GitHub.