views:

139

answers:

4

I'm diving into iOS development while trying to grasp Objective-C and I'm still in that phase where, ever where I look, I see things that don't make any sense to a veteran C programmer like myself. In this Game Kit example on Apple's dev site, one of the header files declares a class interface, three different times...

@interface SessionManager : NSObject <GKSessionDelegate> {
    NSString *sessionID;
    GKSession *myGKSession;
    NSString *currentConfPeerID;
    NSMutableArray *peerList;
    id lobbyDelegate;
    id gameDelegate;
    ConnectionState sessionState;
}

@property (nonatomic, readonly) NSString *currentConfPeerID;
@property (nonatomic, readonly) NSMutableArray *peerList;
@property (nonatomic, assign) id lobbyDelegate;
@property (nonatomic, assign) id gameDelegate;

- (void) setupSession;
- (void) connect:(NSString *)peerID;
- (BOOL) didAcceptInvitation;
- (void) didDeclineInvitation;
- (void) sendPacket:(NSData*)data ofType:(PacketType)type;
- (void) disconnectCurrentCall;
- (NSString *) displayNameForPeer:(NSString *)peerID;

@end

// Class extension for private methods.
@interface SessionManager ()

- (BOOL) comparePeerID:(NSString*)peerID;
- (BOOL) isReadyToStart;
- (void) voiceChatDidStart;
- (void) destroySession;
- (void) willTerminate:(NSNotification *)notification;
- (void) willResume:(NSNotification *)notification;

@end

@interface SessionManager (VoiceManager) <GKVoiceChatClient>

- (void) setupVoice;

@end

I see that each interface is different, but specify the same class name.

  1. What's the reason for this?
  2. I've also noticed this same behavior in other code examples, only instead of declaring multiple interfaces in the header file, you'll see an additional @interface block declared towards the top of the .m implementation file, typically above the @implementation block. Why?

Thanks so much in advance for your wisdom!

+8  A: 

These are called Categories, and you can see them by the parentheses after the class name.

They're used to group methods into chunks instead of having them all in one big bunch. They can also be placed separate from the main class declaration. This is particularly useful inside .m files, where you may need to create utility methods for your class, but you don't want them visible to other objects for any reason (so you don't put them in the .h, which is imported by the other classes). Another common use is to group methods which correspond to a certain logical category, informal protocol, or what have you. Categories can be named (@interface MyClass (MyCategory)) or anonymous (@interface MyClass ()). The latter is usually used for generic private methods in your header.

(The reason you need categories to declare private methods in your .m is so the compiler knows about the methods — otherwise, you'll get a warning when you try to call such a method.)

Also, you can use categories to add methods to existing classes. For example, UIKit contains a category on NSString called NSString(UIStringDrawing). Or if you wanted to make your own:

@interface NSString (MyFoo)
+ (NSString *)fooString;
@end
//... somewhere else...
@implementation NSString (MyFoo)
+ (NSString *)fooString { return @"foo!"; }
@end

Note that you can't add instance variables with a category.

jtbandes
+1  A: 

This is for code upkeep purposes i belive...its easier to looks through the different labeled interfaces, for example (VoiceManager) which is for voice manager setup and methods related to that, and you have the one interface dealing with the GK delegate methods and whatever interaction with gamekit there will be...as opposed to looking at one huge interface file and having to pick out what you are looking for...They can also divide the implementations in this way too so its easier to look through and navigate.

Daniel
+4  A: 

It is not defining the interface 3 times - there is only one interface.

what you are seeing are categories that add methods to the class

There is a base interface that defines the attributes and some methods - there is only one of these ant it defines how the object is stored in memory and is the only one that is needed.

Objective C looks up methods at run time. These methods do not need to be found at compile time and thus do not need to be declared in headers/interfaces etc. If they are not declared and you code calls them then you will get compile time warnings.

In this case one category with an empty name is used for private functions. I usually only put this interface in the .m file of the class so is not visible to other code as not in a header.

The second category is to add the methods to make SessionManager meet the GKVoiceChatClient protocol. The usual reason for doing this is to group code covering a specific behaviour together.

Another reason for using categories is to add methods to an existing class like NSString -you can create your own category adding methods without subclassing the class as you have to do in many other OO languages including Java and C++

Mark
+1  A: 

In order:

The first interface declaration is the actual interface declaration that declares the class as a subclass of NSObject and implementing the GKSessionDelegate protocol. It also declares the instance variables and a selection of methods.

The second interface declaration is a class extension. It can be thought of as a kind of anonymous category. So we'll skip it for now and come back to it.

The third interface is a category declaration. Categories allow you to do two things. They allow you to split the class implementation across multiple source files. In the above, you'll have

@implementation SessionManager

// methods declared in the first @interface

@end

@implementation SessionManager(VoiceManager)

// methods declared in the third @interface

@end

The two @implementations need not be in the same source file.

The other thing a category can do is allow you to extend already existing classes.e.g. @interface NSString(MyStringMethods)...

Coming back to the class extension, this is a bit like an anonymous category. The implementations of the methods declared in it must be in the main @implementation block. The purpose of the class extension is to allow you to declare private API separately from the class's header file. I normally put one in the.m file at the top if I have methods that should only be used from the class. Although, note that this is only a compile time restriction. There is nothing to stop a class extension message from being sent by anybody at run time.

JeremyP