7

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?

jscs
  • 63,694
  • 13
  • 151
  • 195
Fabrizio Prosperi
  • 1,398
  • 4
  • 18
  • 32

3 Answers3

14

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
jscs
  • 63,694
  • 13
  • 151
  • 195
  • Thank you very much Josh. Very well explained. I didn't know with latest Xcode the implementation order was not important anymore, I'll go this way. Cheers. – Fabrizio Prosperi Mar 12 '12 at 23:18
  • Hi I declare the second category in the first category implementation file (just like you showed here). But I did not write a @implementation for the second category, I just implement them in the first category implementation. Xcode did not complain about it, so is that a "better" option ? B/C my goal here is to declare those private methods and I can save some typing. – Qiulang Jul 08 '15 at 06:49
1

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?

fbernardo
  • 10,016
  • 3
  • 33
  • 46
0

@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
Aram Kocharyan
  • 20,165
  • 11
  • 81
  • 96
  • That's exactly my question, not an answer though. I am asking whether I can implement some "private" methods and instance variables which are not exposed externally apart from the class itself the way we do for classes with no categories. I know how to write an interface for my class category but that is exposed outside, I am wondering if there is a syntax to avoid exposing methods outside also for named class categories. – Fabrizio Prosperi Mar 12 '12 at 10:03
  • This comment doesn't make sense. You can have methods that are "private" to your class by declaring the interface() in your implementation file. You can create categories on specific instances/classes that are usable from any classes. What else do you need? – Rog Mar 12 '12 at 10:13
  • @Rog: I want to be able to have private methods in the ClassA+CategoryA.m file which are not exposed to other classes. If your post is suggesting that instead I put them in the ClassA.m file I might agree, but I wanted to know if I was allowed to put them in the proper .m file for each of the categories. – Fabrizio Prosperi Mar 12 '12 at 10:27
  • I've had quite a few problems with defining categories in separate .m and .h files without classes residing in them (static library): http://stackoverflow.com/questions/6029921/clarify-objective-c-categories-in-separate-file – Aram Kocharyan Mar 12 '12 at 10:31
  • In your update above we are almost there Aram thanks, but that prevents me to have other methods of the category exposed (in the .h file). My idea was to have classA+categoryA.h with the @interface ClassA (CategoryA) for exposed methods and classA+categoryA.m with another interface (I don't know the syntax for it) for methods I wanted to keep private to the class itself. – Fabrizio Prosperi Mar 12 '12 at 10:45
  • Yes that would also work. The .m would include a category interface and implantation for (hidden) and also for the public category interface from the .h file – Aram Kocharyan Mar 12 '12 at 11:03
  • mmmh, not really. If I got your idea correctly I can not implement `@interface ClassA (CategoryA)` in both the .h and .m file, xCode gives me the error "Duplicate definition of category 'CategoryA' on interface 'ClassA'" If this is possible, there must be a different syntax. – Fabrizio Prosperi Mar 12 '12 at 11:19
  • oh, and if you were instead suggesting to have a category named 'hidden' that's not feasible, I have introduced categories to handle different logic, a category only to keep things hidden is not what I was looking for. – Fabrizio Prosperi Mar 12 '12 at 11:23
  • The name of the category wouldn't matter, provided it is only interfaced and implemented in the `.m` – Aram Kocharyan Mar 12 '12 at 11:58
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/8782/discussion-between-fabrizio-prosperi-and-aram-kocharyan) – Fabrizio Prosperi Mar 12 '12 at 13:11