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.
Related
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.
What is the best way to make an iVar writeable by its owning class, but readonly by other classes?
Is there more than one way?
The only way I can think of is to set a #property (readonly) for the iVar, but not use dot notation inside the owning class (which I'm guessing wouldn't work with readonly), and in the owning class just reference it directly, without dot notation, then in other class, reference it with dot notation.
But I'm curious if there are other methods.
You can make a property read/write just for the implementation. You do this using a class extension, or anonymous category, in your implementation. Easier to demonstrate so, for example, in your .h:
#interface MyClass
#property (readonly) NSInteger age;
...
#end
and in your .m:
#interface MyClass () // class extension/anonymous category for private methods & properties
// implementation of these go in main #implementation
#property (readwrite) NSInteger age;
...
#end
#implementation MyClass
#synthesize age;
...
#end
Now you can use the property within your class read/write and from outside as read-only.
You do the same for private methods.
[Note: This is Objective-C so the above isn't bullet proof - there are ways around it to call the properties setter - but the compiler will flag errors which assign to the property from outside the class.]
After comments:
[Note 2: an anonymous category/class extension is very similar to a named category. The former must be implemented in the main implementation, the latter may be implemented in an #implementation classname (categoryname) or in the main implementation. Furthermore only the main interface, the interface for a class extension/anonymous category, and the main implementation may declare instance variables; interfaces and implementations for named categories may not. Properties may be declared in both anonymous and named categories, however they may only be synthesized in the main implementation and not in named category implementations. This note is correct at press time, some of these features have changed as Objective-C/Xcode develops.]
Use the private specifier for ivars and a class extension to define the accessor - that way the ivar is private to the class.
// FooClass.h
#interface FooClass : NSObject {
#private int boo;
}
#end
// FooClass.m
#interface FooClass () {
}
#property (nonatomic,assign) int boo;
#end
#implementation FooClass
#synthesize boo;
// ....
#end
If I have this class defined, how do I access the someObject property in subclasses without compiler errors?
#interface MyBaseClass
// someObject property not declared here because I want it to be scoped
// protected. Only this class instance and subclass instances should be
// able to see the someObject property.
#end
// This is a private interface extension...properties declared here
// won't be visible to subclasses. However, I don't see any way to
// declare protected properties...
#interface MyBaseClass (private)
#property (nonatomic, readwrite, retain) NSObject *someObject;
#end
#interface MySubclass : MyBaseClass
#end
#implementation MySubclass
- (id) init {
// Try to do something with the super classes' someObject property.
// Always throws compile errors.
// Semantic Issue: Property 'someObject' not found
// object of type 'MySubclass *'
self.someObject = nil;
}
#end
I'm obviously not understanding how inheritance works in objective-c. Could someone enlighten me?
The solution you're after is to declare the MyBaseClass private property in a class extension:
#interface MyBaseClass ()
#property (nonatomic, readwrite, retain) NSObject *someObject;
#end
You are then free to make that declaration both in MyBaseClass and in MySubclass. This lets MySubclass know about these properties so that its code can talk about them.
If the repetition bothers you, put the class extension in a .h file of its own and import it into both .m files.
I will give an example from my own code. Here is MyDownloaderPrivateProperties.h:
#interface MyDownloader ()
#property (nonatomic, strong, readwrite) NSURLConnection* connection;
#property (nonatomic, copy, readwrite) NSURLRequest* request;
#property (nonatomic, strong, readwrite) NSMutableData* mutableReceivedData;
#end
There is no corresponding .m file and that's all that's in this file; it is, as it were, purely declarative. Now here's the start of MyDownloader.m:
#import "MyDownloader.h"
#import "MyDownloaderPrivateProperties.h"
#implementation MyDownloader
#synthesize connection=_connection;
#synthesize request=_request;
#synthesize mutableReceivedData=_mutableReceivedData;
// ...
And here's the start of its subclass MyImageDownloader.m:
#import "MyImageDownloader.h"
#import "MyDownloaderPrivateProperties.h"
Problem solved. Privacy is preserved, as these are the only classes that import MyDownloaderPrivateProperties.h so they are the only classes that know about these properties as far as the compiler is concerned (and that's all that privacy is in Objective-C). The subclass can access the private properties whose accessors are synthesized by the superclass. I believe that's what you wanted to accomplish in the first place.
that's how you access them. how you declare them is what's biting you:
#interface MyBaseClass : NSObject
#property (nonatomic, readwrite, retain) NSObject *someObject;
#end
this is the normal way to declare a new objc class.
by adding the parentheses (instead of declaring the superclass - NSObject in this case), you have declared a class extension, which is probably not visible to the subclass (via inclusion).
you will probably never need to declare a root class in objc:
#interface MyBaseClass // << superclass omitted
#property (nonatomic, readwrite, retain) NSObject *someObject;
#end
NSObject (or a subclass of, assuming you're target apple's systems) should be the base class unless you're very experienced and know what a root class is for.
class extensions are often used to 'simulate' private interfaces. by simulate, the compiler doesn't enforce this, as it would be enforced in other languages. for example, all messages are still dynamic, although the subclass may (unknowingly) override methods in your extensions, if declared with the same selector.
Judging by the () after your base class name, it looks like you are declaring a private interface extension within your class implementation, is this the case? If so the variable will only be accessible from within that class implementation.
Does your MyBaseClass inherits from NSObject directly?
If so, you need to declare the someObject property in your interface file, as in:
#interface MyBaseClass : NSObject
{
}
#property (nonatomic, retain) NSObject *someObject;
And then synthesize it like you are already doing.
This is an alternative that meets most of the objectives.
In your header, define the interface
#interface MyBaseClass : NSObject {
NSObject *inheritableObject;
}
#property (readonly) NSObject *inheritableObject;
Now you can edit the inheritableObject in MyBaseClass as well as in any Class that inherits from MyBaseClass. However, from the outside, it is readonly. Not private as in the case of #interface MyBaseClass(), but protected from uncontrolled changes.
super.someObject = nil;. Inheritance means MyBaseClass is your super class.
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.
Is it possible to define properties that are only available to the class they are defined in, and that class's subclasses?
Stated another way, is there a way to define protected properties?
Technically, no. Properties are really just methods, and all methods are public. The way we "protect" methods in Objective-C is by not letting other people know about them.
Practically, yes. You can define the properties in a class extension, and still #synthesize them in your main implementation block.
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.
You could use such syntax in subclass implementation.
#interface SuperClass (Internal)
#property (retain, nonatomic) NSString *protectedString;
#end
You can use a category to gain your purpose
#interface SuperClass (Protected)
#property (nonatomic, strong) UIImageView *imageView;
#property (nonatomic, strong) UIView *topMenuView;
#property (nonatomic, strong) UIView *bottomMenuView;
#end
In Subclass, you import this category in the file .m