All-
What is the proper way to define friend properties in Obj-C (specific implementation is Xcode for IOS). By friend properties, I mean instance attributes that are available to the base class, any subclasses of that base, but not to the public.
example:
#interface Base : NSObject
#property int friend
#end
#interface Sub : Base
#end
#implementation Base
#synthesize friend;
#end
#implementation Sub
-(id)newFriend
{
[self setFriend: [someOtherObject friend]]; // this should be allowed
}
#implementation Wow
-(void)
{
Sub* sub = [[Sub alloc] init];
[sub setFriend: [someOtherObject friend]]; // this should not be allowed
}
I have tried putting the #property friend for Base in the .m file, but then Sub cannot see it.
In c++ I had a helper declarator called friend which did exactly what I am looking for. How to do this in Obj-C?
Thanks
There's no way to enforce this behaviour in Objective-C. Properties are just getter and setter methods on the Objective-C object, and methods can be called by anyone.
One thing you can control is visibility. The way that I've emulated protected properties in the past is to declare only public properties in Base.h. Then create another header file called Base+protected.h, which contains a class extension with the declared property.
// Base.h
#interface Base : NSObject
#end
// Base+protected.h
#import "Base.h"
#interface Base ()
#property int friend;
#end
// Base.m
#import "Base.h"
#import "Base+protected.h"
#implementation Base
#synthesize friend;
#end
// Sub.h
#import "Base.h"
#interface Sub : Base
#end
// Sub.m
#import "Base+protected.h"
#implementation Sub
#end
So the Sub implementation can see the protected properties, but any external class only #includes Base.h or Sub.h, so cannot see them.
Related
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?
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).
This question already has answers here:
Closed 10 years ago.
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?
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.
I wish I could tell you otherwise but you just can't. See this question for more information: Protected methods in Objective-C.
I am not sure, what you want to do? Something Hacking or Cracking of Data Abstraction out of OOPS concept?
Extensions are used to add properties. You have successfully added private property as in
#import "BGGoogleMap.h"
#interface BGGoogleMap ()
#property (strong,nonatomic) NSString * protectedHello;
#end
What are you doing in this ?
#import "BGGoogleMap+protected.h"
#implementation BGGoogleMap ()
-(NSString *) protectedHello
{
return _
}
#end
You have extended a class, now you are again implementing same class !!! Twice!!! And category only comes with .h file. I guess you are creating yourself a .m file, that not acceptable.
Private properties cant be accessed outside the class, it can be accessed only from the base class or subclass. That is what the error is.
I can't implement class extension outside the original .m files it seems.
Yes this is abstraction and data hiding of Objective-c !!!
According to some official talk, a class in Objective-C should only expose public methods and properties in its header:
#interface MyClass : NSObject
#property (nonatomic, strong) MyPublicObject *publicObject;
- (void)publicMethod;
#end
and private methods/properties should be kept in class extension in .m file:
#interface MyClass()
#property (nonatomic, strong) MyPrivateObject *privateObject;
- (void) privateMethod;
#end
and I don't think there is a protected type for things that are private but accessible from subclasses. I wonder, is there anyway to achieve this, apart from declaring private properties/methods publicly?
One way to solve this is to re-declare the property in your subclass's class extension, and then add an #dynamic statement so that the compiler won't create an overriding implementation of that property. So something like:
#interface SuperClass ()
#property (nonatomic, strong) id someProperty;
#end
....
#interface SubClass ()
#property (nonatomic, strong) id someProperty;
#end
#implementation SubClass
#dynamic someProperty;
#end
This obviously isn't ideal because it duplicates a privately visible declaration. But it is quite convenient and helpful in some situations so I'd say evaluate on a case-by-case basis the dangers involved in this duplication vs. exposing the property in the public interface.
An alternative - that is used by Apple in UIGestureRecognizer - is to declare the property in a separate category header file explicitly named as "private" or "protected" e.g. "SomeClass+Protected.h". That way, other programmers will know they ought not import the file. But, if you don't control the code you're inheriting from, that's not an option.
This is possible by using a class extension (not category) that you include in the implementation files of both the base class and subclasses.
A class extension is defined similar to a category, but without the category name:
#interface MyClass ()
In a class extension, you can declare properties, which will be able to synthesize the backing ivars (XCode > 4.4 automatic synthesis of the ivars also works here).
In the extension class, you can override/refine properties (change readonly to readwrite etc.), and add properties and methods that will be "visible" to the implementation files (but note that the properties and methods aren't really private and can still be called by selector).
Others have proposed using a seperate header file MyClass_protected.h for this, but this can also be done in the main header file using #ifdef like this:
Example:
BaseClass.h
#interface BaseClass : NSObject
// foo is readonly for consumers of the class
#property (nonatomic, readonly) NSString *foo;
#end
#ifdef BaseClass_protected
// this is the class extension, where you define
// the "protected" properties and methods of the class
#interface BaseClass ()
// foo is now readwrite
#property (nonatomic, readwrite) NSString *foo;
// bar is visible to implementation of subclasses
#property (nonatomic, readwrite) int bar;
-(void)baz;
#end
#endif
BaseClass.m
// this will import BaseClass.h
// with BaseClass_protected defined,
// so it will also get the protected class extension
#define BaseClass_protected
#import "BaseClass.h"
#implementation BaseClass
-(void)baz {
self.foo = #"test";
self.bar = 123;
}
#end
ChildClass.h
// this will import BaseClass.h without the class extension
#import "BaseClass.h"
#interface ChildClass : BaseClass
-(void)test;
#end
ChildClass.m
// this will implicitly import BaseClass.h from ChildClass.h,
// with BaseClass_protected defined,
// so it will also get the protected class extension
#define BaseClass_protected
#import "ChildClass.h"
#implementation ChildClass
-(void)test {
self.foo = #"test";
self.bar = 123;
[self baz];
}
#end
When you call #import, it basically copy-pastes the .h file to where you are importing it.
If you have an #ifdef, it will only include the code inside if the #define with that name is set.
In your .h file, you don't set the define so any classes importing this .h wont see the protected class extention.
In the base class and subclass .m file, you use #define before using #import so that the compiler will include the protected class extension.
While the other answers are correct, I'd like to add...
Private, protected and public are available for instance variables as such:
#interface MyClass : NSObject {
#private
int varA;
#protected
int varB;
#public
int varC;
}
#end
Your only choice is to declare it as public in the header file. If you want to at least keep some method separation, you can create a category and have all your protected methods and attributes there, but in the end everything will still be public.
#import "MyClass.h"
#interface MyClass (Protected)
- (void) protectedMethods;
#end
Simply create a .h file with your class extension. Import this into your .m files. Incidentally, this is a great way to test private members without breaking encapsulation (I'm not saying you should test private methods :) ).
// MyClassProtectedMembers.h
#interface MyClass()
#property (nonatomic, strong) MyPrivateObject *privateObject;
- (void) privateMethod;
#end
/////////////////
#import "MyClassProtectedMembers.h"
#implementation MyClass
// implement privateMethod here and any setters or getters with computed values
#end
Here's a gist of the idea: https://gist.github.com/philosopherdog/6461536b99ef73a5c32a
I see good answers for making properties visible, but I don't see exposing the methods addressed very clearly in any of these answers. Here is how I have successfully exposed private methods to the subclass using a Category:
SomeSuperClass.m:
#implementation SomeSuperClass
-(void)somePrivateMethod:(NSString*)someArgument {
...
}
SomeChildClass.h
#interface SomeChildClass : SomeSuperClass
SomeChildClass.m
#interface SomeSuperClass (exposePrivateMethod)
-(void)somePrivateMethod:(NSString*)someArgument;
#end
#implementation SomeChildClass
-(void)doSomething {
[super somePrivateMethod:#"argument"];
}
#end
That's because there's not even a real distinction between private and public. While the compiler may warn you about an interface missing a certain method or instance variable, your program will still work.
I am trying to declare a private #interface for a category, in the .m file.
For a normal class I would do:
#interface ClassA ()
#end
#implementation ClassA
#end
and it would work smoothly.
For a class with categories I tried:
#interface ClassA (CategoryA) ()
#end
#implementation ClassA (CategoryA)
#end
but it is giving all sort of different errors. I am trying to "extend" a category, the way that a class is extended via this syntax #interface ClassA ().
I want to have private methods for the category, and I wanted to know if, IN ADDITION to the exposed interface I am allowed to put a second category #interface in the .m file, which does not expose instance variables and methods outside the class itself.
Something like this:
ClassA+categoryA.h
#interface ClassA (CategoryA)
<some public methods>
#end
ClassA+categoryA.m file
#interface ClassA (CategoryA)
<some private methods>
#end
#implementation ClassA (CategoryA)
<here I want to be able to call the private methods above>
#end
Right now this is giving me a warning in Xcode:
Duplicate definition of category 'CategoryA' on interface 'ClassA'
Is there any way to get this behavior?
No, you can't declare two interfaces for a single category. You can do one of two things:
Englebert+Humperdinck.h
#import "Englebert.h"
#interface Englebert (Humperdinck)
- (void) croon;
#end
You can declare another category with a different name to contain the private methods. These can then be used in the same file where the private category interface is declared:
Englebert+Humperdinck.m
#import "Englebert+Humperdinck.h"
#interface Englebert (HumperdinckPrivate)
- (void) warmUp;
#end
#implementation Englebert (HumperdinckPrivate)
- (void)warmUp {
NSLog(#"Warm up");
}
#end
#implementation Englebert (Humperdinck)
- (void)croon {
[self warmUp];
NSLog(#"Croon");
// etc.
}
#end
The other option is to simply not declare the private methods. If you just define them in the implementation block, you can use them at any point in that file after they are defined (and for the latest version of Xcode/LLVM, the order is in fact unimportant -- undeclared methods can be used anywhere in the file in which they are defined). No other files will be able to see these methods.
Englebert+Humperdinck.m
#import "Englebert+Humperdinck.h"
#implementation Englebert (Humperdinck)
/* Undeclared private method */
- (void)warmUp {
NSLog(#"Warm up");
}
- (void)croon {
[self warmUp];
NSLog(#"Croon");
// etc.
}
#end
Do
#interface ClassA (CategoryA)
#end
#implementation ClassA (CategoryA)
#end
Instead. Categories can't have instance varibles. And what kind of errors are you talking about?
#interface ClassA () is an anonymous category, and you can use these as interfaces and define the implementation in the ClassA implementation as well. #interface ClassA (CategoryA) () is a syntax error and should read #interface ClassA (CategoryA)
EDIT:
To create private methods for a class, in that class' .m file you would have:
#interface ClassA ()
// Private functions declared
#end
#implementation ClassA
// Private functions defined
// Other functions defined
#end
The same can be done for named categories, however you will need to define the implementation separately to avoid warnings. Again, in the .m file:
#interface ClassA (hidden)
// Private functions declared
#end
#implementation ClassA (hidden)
// Private functions declared
#end
#implementation ClassA
// Other functions defined
#end
I have the following protocol:
#protocol MyProtocol
#property (nonatomic, retain) NSObject *myProtocolProperty;
-(void) myProtocolMethod;
#end
and I have the following class:
#interface MyClass : NSObject {
}
#end
I have a class extension declared, I have to redeclare my protocol properties here or else I can't implement them with the rest of my class.
#interface()<MyProtocol>
#property (nonatomic, retain) NSObject *myExtensionProperty;
/*
* This redeclaration is required or my #synthesize myProtocolProperty fails
*/
#property (nonatomic, retain) NSObject *myProtocolProperty;
- (void) myExtensionMethod;
#end
#implementation MyClass
#synthesize myProtocolProperty = _myProtocolProperty;
#synthesize myExtensionProperty = _myExtensionProperty;
- (void) myProtocolMethod {
}
- (void) myExtensionMethod {
}
- (void) useMyConsumer {
[[[MyConsumer new] autorelease] consumeMyClassWithMyProtocol:self];
}
#end
MyConsumer will only be called from MyClass, so I don't want any other classes to see that MyClass implements methods on MyProtocol because they aren't public API. Similarly, I don't want MyConsumer to see the class extension within MyClass.
#interface MyConsumer : NSObject {
}
#end
#implementation MyConsumer
- (void) consumeMyClassWithMyProtocol: (MyClass<MyProtocol> *) myClassWithMyProtocol {
myClassWithMyProtocol.myProtocolProperty; // works, yay!
[myClassWithMyProtocol myProtocolMethod]; // works, yay!
myClassWithMyProtocol.myExtensionProperty; // compiler error, yay!
[myClassWithMyProtocol myExtensionMethod]; // compiler warning, yay!
}
#end
Is there any way I can avoid redeclaring the properties in MyProtocol within my class extension in order to implement MyProtocol privately?
What you've been referring to as an "anonymous category" is actually known as a class extension, and is used to declare private functionality in an implementation file. That last part is important, because it means that other classes will not be able to see the declarations that you put into a class extension (and they won't be able to see that your class implements the methods of MyProtocol). This is also probably what is responsible for #synthesize failing without redeclaring the properties.
Instead, declare your conformance to the protocol in the interface of your class, and add whatever methods you want to be public:
#interface MyClass : NSObject <MyProtocol> {
}
// public methods and properties go here
#end
If you add the protocol declaration to your interface, then it also removes the need for your consumer to specify it explicitly. Your consumer method can have the following signature instead:
- (void) consumeMyClassWithMyProtocol: (MyClass *) myClassWithMyProtocol;
EDIT: It sounds like you're looking for a way to selectively expose private functionality. First, I would try to consider a different architecture for what you're trying to accomplish, because what's about to follow is a rather unpleasant solution, and it's generally better OOP if everything is public or private.
With that said, Apple typically solves this problem by having a separate header file for the class in question, which declares the methods that should be visible. So you would have your class interface, in which you expose everything that should be completely public:
// MyClass.h
#interface MyClass : NSObject {
}
#end
And a separate header, in which you declare a category for pseudo-private stuff:
// MyClass+Private.h
#import "MyClass.h"
#interface MyClass (Private) <MyProtocol>
- (void)mySortaPrivateMethod;
#end
MyClass.m would implement everything from those two files, and could still have a class extension:
// MyClass.m
#import "MyClass.h"
#import "MyClass+Private.h"
#interface MyClass ()
- (void)myClassExtensionMethod;
#end
#implementation MyClass
// everything can go here
#end
Then your consumer would include MyClass+Private.h so that it can see the declarations there, and everyone else would simply use MyClass.h.