What I'd like to do is separate a category's methods into private and public methods. The private methods need to be visible outside of that category's file, but not outside the class.
For instance, let's say I have the following files:
ClassA.m
ClassA.h // <-- Includes definitions of public category methods
ClassAPrivates.h // <-- Includes definition of private category methods.
ClassA+Render.m
ClassAPrivates.h would look like this:
#interface ClassA()
// private methods here, for use inside ClassA
#end
#interface ClassA(Render)
// the private methods of the Render category.
-(void)privateConfigureDeviceContext;
-(void)privateConfigureBufferSpace;
#end
And 'ClassA.h' would look like this:
#interface ClassA : NSObject
// public methods of ClassA
#end
#interface ClassA (Render)
// public methods of category Render
-(void)drawLine;
-(void)drawCircle;
#end
However, XCode complains about duplicate interface for Render. Any workarounds?
No need to create separate categories for private methods.
There are 3 scenarios:
ClassA+Render.m accessing the private methods that are defined in the ClassA.m
ClassA.m accessing the private methods that are defined in ClassA+Render.m
ClassA+Render.m accessing the properties defined in ClassA.m
Note: Though the original poster hasn't asked for Scenario 3, I thought it might come in handy
Example
ClassA.m defines 2 private methods
basePrivateMethod1 - This would invoke renderPrivateMethod1
basePrivateMethod2
ClassA+Render.m defines 2 private methods
renderPrivateMethod1
renderPrivateMethod2 - This would invoke basePrivateMethod2
Scenario 1
In ClassA+Render.m just create an extension and forward declare the methods that you want to use.
ClassA+Render.m
#interface ClassA ()
- (void) basePrivateMethod1; //Just forward declare the method
#end
Scenario 2
In ClassA+Render.h just declare the methods defined in ClassA+Render.m.
In ClassA.m just include (#import) ClassA+Render.h
Pls note, ClassA+Render.h would / should not be used outside of ClassA
ClassA+Render.h
#interface ClassA (Render)
{
- (void) basePrivateMethod2;
}
Scenario 3
Pls note - Properties can not be defined / created in the categories
So create a new category called ClassA+Properties
In ClassA+Properties.h redeclare all the properties
In ClassA+Properties.m use #dynamic for all the properties to tell the compiler that the actual definition of these properties is else where
In ClassA+Render.m include (#import) ClassA+Properties.h, so that all the properties are accessible
ClassA+Properties.h
#interface ClassA (Properties)
{
#property NSUInteger property1;
#property NSUInteger property2;
}
ClassA+Properties.m
#interface ClassA (Properties)
{
#dynamic property1;
#dynamic property2;
}
You declare two categories named Render on ClassA. That is why Xcode is complaining.
Category is a concept which states any new method can be added to an existing class and all subclass will eventually get that method.
You use categories to define additional methods of an existing class—even one whose source code is unavailable to you—without subclassing. You typically use a category to add methods to an existing class, such as one defined in the Cocoa frameworks. The added methods are inherited by subclasses and are indistinguishable at runtime from the original methods of the class.
If you are adding private method, then why to go with category? As it can be used only by that single class file itself. Therefore why not to create a normal private method!!!
You can declare Render's private methods in a class extension in its own file. This takes advantage that you can have multiple class extensions.
ClassA.h:
#interface ClassA : NSObject
// public methods of ClassA
#end
ClassA+Render.h:
#interface ClassA (Render)
// public methods of category Render
-(void)drawLine;
-(void)drawCircle;
#end
ClassA+Render_Private.h:
#interface ClassA ()
// the private methods of the Render category.
-(void)privateConfigureDeviceContext;
-(void)privateConfigureBufferSpace;
#end
Related
I know about the possibility of declaring private properties on a class by putting them inside an unnamed category on that class declared in the implementation (.m) file of that class. That's not what I want to do.
I'm dealing with a named category on a class that adds some functionality to that class. For this functionality, it would help me very much to have a private property to use in my category - so the usual way of achieving this (described above) doesn't seem to work for me. Or does it? Please enlighten me!
Inside your category's implementation file, declare another category and call it something like MyCategoryName_Private, and declare your private properties there. Provide implementations of the -propertyName and -setPropertyName: methods using associated objects.
For example, your implementation file might look like this:
#import "SomeClass+MyCategory.h"
#import <objc/runtime.h>
#interface SomeClass (MyCategory_Private)
#property (nonatomic, strong) id somePrivateProperty;
#end
#implementation SomeClass (MyCategory_Private)
static void *AssociationKey;
- (id)somePrivateProperty
{
return objc_getAssociatedObject(self, AssociationKey);
}
- (void)setSomePrivateProperty:(id)arg
{
objc_setAssociatedObject(self, AssociationKey, arg, OBJC_ASSOCIATION_RETAIN);
}
#end
#implementation SomeClass (MyCategory)
// implement your publicly declared category methods
#end
I've been trying to find a workaround to declare #protected properties in Objective-C so only subclasses in the hierarchy can access them (read only, not write).
I read that there is no documented way of doing this so I thought of this workaround and I wanted to ask StackOverflow's opinion about it.
Every custom class at the top of the hierarchy contains three classes, one implementation and two interfaces.
Let's name them:
ClassA.h
ClassA_protected.h
ClassA.m
Then any subclass of this ClassA would be as usual:
ClassB.h
ClassB.m
First I created the interface ClassA.h where I declare a protected int variable so any subclass of ClassA can have access to it:
#interface ClassA : NSObject{
#protected
int _myProtectedInt;
}
#end
Next step is the workaround I was talking about. However, once you read it you will see that it is quite straight forward. I declared a second interface called ClassA_protected.h which actually works as an extension of ClassA.h and allows us to tag the property as readonly:
#import "ClassA.h"
#interface ClassA ()
#property (nonatomic , readonly) int myProtectedInt;
#end
Last step of preparing the protected hierarchy is to declare its implementation in ClassA.m where we only synthesize our property:
#import "ClassA_protected.h"
#implementation ClassA
#synthesize myProtectedInt = _ myProtectedInt;
#end
This way, every class that needs to be a subclass of ClassA.h, will import ClassA_protected.h instead. So a child like, for example ClassB.h, would be as follows:
#import "ClassA_protected.h"
#interface ClassB : ClassA
#end
And an example of accessing this property from ClassB.m's implementation:
#implementation ClassB
-(void) method {
//edit protected variable
_myProtectedInt= 1;
//normal access
self.muProtectedInt;
}
#end
Sure, that works fine. Apple uses the same approach for example in the UIGestureRecognizer class. Subclasses have to import the additional UIGestureRecognizerSubclass.h file and override the methods that are declared in that file.
For simple "properties" just use ivar instead. That's as good as properties for all practical purposes.
Moreover, the default is already protected.
If you ask for opinion, this is mine: If one decides to mutate your
_myProtectedInt
he will probably succed anyway, because it's definitely possible with Objective-C runtime. Except this, your solution is quite OK.
Import the protected header in the implementation only. e.g.
ClassB.h
#import "ClassA.h"
#interface ClassB : ClassA
#end
ClassB.m
#import "ClassA_protected.h"
#implementation ClassB
#end
And in a framework the protected header should be marked project so it is not included in the public headers of the framework. Apple usually use the suffix _Internal.h for their protected methods.
For init or overriding a lazy loaded get property you would need direct access to the #proteced ivar, however for your use it would be better to redeclare the property as readwrite instead then you can take advantage of any features of the setter, atomicity for example.
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
The following is an example of how to create 'private' methods in Objective-C:
MyClass.m
#import "MyClass.h"
#import <stdio.h>
#implementation MyClass
-(void) publicMethod {
printf( "public method\n" );
}
#end
// private methods
#interface MyClass (Private)
-(void) privateMethod;
#end
#implementation MyClass (Private)
-(void) privateMethod {
printf( "private method\n" );
}
#end
Why does this example use the (Private) category name in the #interface MyClass (Private) and #implementation MyClass (Private) declarations? Aren't interfaces declared in .m files automatically private? Do we need 2 different implementation blocks?
Is there a variation of this technique to create 'protected' methods?
(I'm new to objective-C. Am I missing the point of category names? Why do we need to use them in this instance?)
Thanks-
If you're creating a category, you have to give it some name. The name Private for the category doesn't have any special meaning to the language.
Also, in modern Objective-C, this would be a better case for a class extension, which is declared like a nameless category (#interface MyClass ()) but the implementation goes in the main #implementation block. This is a relatively recent addition, though. The secret-category method in your example is the more traditional way of accomplishing roughly the same thing.
The (Private) part declares a category for MyClass. The category name can be anything, but all categories interpreted by the compiler will add the methods from that category to the list of known methods for that class as a whole. By putting the private methods in a category at the top of a source file, you can declare the public interface to your class in your main .h file but all of your private methods will be “hidden” inside the .m file, all the while you are still able to use those methods without the compiler warning that the methods weren't found.
The category name is only to “categorise” the methods of a class. You can categorise them any way you want. The name is not important, simply calling the category Private does not make those methods private, they are still able to be overridden by subclasses and able to be discovered by introspection.
Basically, you are just separating the public interface from your class from your private interface. If you have to ship a library with its headers, the headers will not contain any of the private methods.
Typically though, the private interface is placed at the top of the source file so that the compiler interprets it (and finds all the methods that the category provides) before interpreting your main and private implementations.
I'm seeing some code I've inherited that looks like the following:
#interface SomeClass (private)
This is within SomeClass.m, the implementation file. There is an accompanying header file which doesn't suggest that the class is using a category. Is (private) in this case just a poor name given to a category for SomeClass? And I'm assuming it's perfectly legitimate to specify categories such as these in an implementation?
It isn't the name "private" that makes it private; the methods are private because they are in a category declared within the implementation file.
There are three uses of a category, each of which add methods to a class (note: methods only, not iVars)
Extending an existing Cocoa class
This lets you add your own methods to an existing class.
For example, if you want to extend NSString to apply special capitalization, you could create a new class called, say NSString+Capitals. in the NSString+Capitals.h you would have:
#interface NSString (Capitals)
-(NSString *)alternateCaps:(NSString *)aString;
#end
and in NSString+Capitals.m you would implement the method
#implementation NSString (Capitals)
-(NSString *)alternateCaps:(NSString *)aString
{
// Implementation
}
Private methods on a class
This is the same as above, except that the extra methods are declared and defined in the implementation file (.m) Usually a way of having private methods - because they are not in the .h file (which is the one #imported by other classes) they are simply not visible. In this case, the implementation of the methods are done in their own implementation block. e.g
// someClass.m
#interface someClass (extension)
-(void)extend;
#end
#implementation someClass
// all the methods declared in the .h file and any superclass
// overrides in this block
#end
#implementation someClass (extension)
-(void)extend {
// implement private method here;
}
Class Extension (New for 10.5 Leopard)
A simpler way of having private methods. In this special case, the category name is empty and the private methods are implemented in the same block as all the other class methods.
// someClass.m
#interface someClass ()
-(void)extend;
#end
#implementation someClass
// all the methods declared in the .h file and any superclass
// overrides in this block
// Implement private methods in this block as well.
-(void)extend {
// implement private method here;
}
#end
Here's a link to the Apple docs on Categories and extensions.
"Private" is just a name that suggests the methods are not public and are used for the internal implementation of the class, but there's nothing in the declaration of the category that enforces that.
Also, methods defined in a category are added to the class definition at runtime, so the accompanying header file need not declare that it is using a category -- it gets "used" automatically.
I use that to give me somewhere to declare (and thus document, as well as shut the compiler up about) helper methods which don't need to be in the public interface. Then the "consumers" of the class are (tacitly, given that there's nothing stopping them other than good manners) restricted to using methods defined in the header file.