views:

1770

answers:

7

I'm on a project doing an iPhone application. We had a Cocoa consultant come in for a few weeks. He showed me an interesting idiom of Cocoa, dealing with interfaces, but there was a difficult language barrier between us, and he wasn't really able to explain why this was done or where it was documented so I could learn more on my own. I went into monkey see mode, and just used the style he prefers. But it's been bugging the hell out of me that I don't know more about the history of this style. It's certainly NOT an informal protocol. Sure enough, looking at some of the Cocoa API headers, I sometimes see the style he asserted was the 'Cocoa' way. Here's an example (note accessors, mutators, etc., each have their own interface declaration without the funny braces):

@interface AViewController : UIViewController <UITextViewDelegate> {

@public
    UITableView *tableView;
@private
    NSUInteger someIndex;
}

@property (nonatomic, retain) ...
@end

@interface AViewController (AViewControllerCreation)

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil withController:(id)controller;

@end

@interface AViewController (AViewControllerMutator)

- (void) doSomeSettingStuff;

@end

@interface AViewController (AViewControllerAccessor)

- (NSString *)doSomeAccessorStuff;

@end

@interface AViewController (AViewControllerAction)

- (IBAction)cancel:(id)sender;

@end

@interface AViewController (AViewControllerTableViewDelegate)  <UITableViewDelegate, UITableViewDataSource>

@end

You can see this style of setting up the interface in NSButton, NSControl, etc. Interestingly, corresponding classes like UIButton, UIControl DON'T use this idiom. Hmm, these probably came after as I assume UIKit was done after AppKit. So is this idiom 'old hat'? Also, is there any reason for it other then style? Is it good style? Bad? Any docs that go over this? Thanks all.

+12  A: 

These are what's known in Objective-C as "categories". Categories make it possible to have multiple @interface and @implementation blocks for the same class. This works even to the extent that you can add methods on classes in the standard Apple frameworks, e.g. adding a category on NSString to add new methods to it. Categories can add methods but not instance variables, so in your example the first @interface is the core class declaration and all of the others are categories on the AViewController class.

It's not "old hat" by any means but your example takes the use of categories to a rather bizarre extreme. Categories make sense wherever it makes logical sense to break up a class's implementation into multiple blocks, for example if the class has a bunch of methods that logically fall into two or more groups. They're also sometimes used to declare pseudo-private methods by putting a category @interface named "private" in the same file as the @implementation. ObjC's dynamic dispatch means there's no such thing as a private method but this approach avoids publishing the names of methods you'd prefer people not to use.

The example above is not actually wrong but it's kind of ridiculous. It suggests that the contractor got the idea that every new method should always have its own category for some reason, which is just not true.

Tom Harrington
Thanks Tom. BTW, I removed a lot of methods so it's purely pedantic. I DO see why he likes to group things (like accessors/mutators). Again, having looked at NSButton vs UIButton headers, I see that both are used. But your answers points out that these are actually categories. Best answer yet Tom!
Rob
A: 

I don't know; those look a lot like informal protocols to me, largely for delegates. See pages 297 - 298 of Cocoa Programming with Mac OS X, 3rd Edition. The protocols are implemented via Categories ... And in all honesty, they appear to be heavily overused in your sample.

John Rudy
That is one way to implement an informal protocol (Obj-C 2.0 has new optional protocol methods too), but things like init methods, accessors and mutators would not be part of a protocol.
Marc Charbonneau
+2  A: 

That example is very strange and would definitely raise a red flag for any experienced Cocoa programmer.

It is a common practice to use categories in the same way to separate private methods from the public implementation, and I've done the same thing in the past to separate private threaded methods from code that runs on the main thread. I can't see any good that would come from separating out all your public methods like that though.

What is a good tool for this situation is the #pragma mark <label> keyword. which allows you to group similar methods in an implementation. I think that's what you're aiming for, although you don't need to go overboard in creating groups. For example, in a window controller class I would have #pragma mark API, #pragma mark NSWindowController Overrides, #pragma mark NSObject Overrides, #pragma mark NSWindow Delegate Methods, and so on. That helps me find and jump to the method I'm looking for very quickly in Xcode, although it's just a matter of style so you can really use it however you see fit.

Marc Charbonneau
Hi Marc. Yeah, I love the #pragma and already use it in xcode it really helps! But it just seems to benefit in organizational ways. Yeah, I didn't come up with this idiom he did! I didn't like it at first, but went along. It IS in NSButton, NSControl, etc., and um, that's Apple's code ;)
Rob
A: 

Regarding John Rudy's response-- It's true that informal protocols are implemented as categories, but they're generally categories on NSObject. Since the informal protocol might be used by almost any object, it needs to be a category on an class that the adopting object inherits from, and pretty much everything is going to inherit from NSObject. You could make a case for an informal protocol as a category on some other class in specific situations, but it's an unusual approach and definitely not "the Cocoa way"

Tom Harrington
A: 

Ok, I believe Tom's answer is the most useful so far. However, as everyone seems to think this an overboard use of categories I again took a look at NSButton. Below is a bastardized version of the header:

@interface NSButton : NSControl <NSUserInterfaceValidations>

- (NSString *)title;
- (void)setTitle:(NSString *)aString;
...
...
@end

@interface NSButton(NSKeyboardUI)
- (void)setTitleWithMnemonic:(NSString *)stringWithAmpersand;
@end

@interface NSButton(NSButtonAttributedStringMethods)
- (NSAttributedString *)attributedTitle;
- (void)setAttributedTitle:(NSAttributedString *)aString;
- (NSAttributedString *)attributedAlternateTitle;
- (void)setAttributedAlternateTitle:(NSAttributedString *)obj;
@end

@interface NSButton(NSButtonBezelStyles)
- (void) setBezelStyle:(NSBezelStyle)bezelStyle;
- (NSBezelStyle)bezelStyle;
@end

@interface NSButton(NSButtonMixedState)
- (void)setAllowsMixedState:(BOOL)flag;
- (BOOL)allowsMixedState;
- (void)setNextState;
@end

@interface NSButton(NSButtonBorder)
- (void) setShowsBorderOnlyWhileMouseInside:(BOOL)show;
- (BOOL) showsBorderOnlyWhileMouseInside;
@end

@interface NSButton (NSButtonSoundExtensions)
- (void)setSound:(NSSound *)aSound;
- (NSSound *)sound;
@end

So if their using categories to organize NSButton into sections: (NSButtonMixedState) (NSButtonBorder) above, that really only have a couple of operations, why is using this for organizing accessors/mutators bad style? Of course my first example was a silly pedantic interface, but the intent of separating groupings of operations is the same.

Rob
A: 

First of all, just because something appears in one of Apple's header files doesn't necessarily mean you should take it as an example of a good way to do things. Apple's developers are human too, and subject to the same limitations of skill and time pressures as anyone else.

Using multiple categories in this way suggests that NSButton's implementation is divided into several source files. This might be because the different aspects of NSButton were coded by different people, or at different times, or possibly some other reason. Regardless the division is likely based in part on how the development team and its processes are organized, with the category system providing a way for the work to be divided. In the ideal setup you'd break things up on logical functional boundaries but in practice other factors can come into play.

Tom Harrington
+1  A: 

One really good reason for having a public category, and one that Apple uses often, is for extending a class with functionality that exists in the category's framework, but which doesn't exist in the framework that defines the class. For example, NSString is defined in Foundation.framework, but categories on NSString defining methods for drawing an NSString to the screen are defined in AppKit.framework.

Another good usage of categories is for dependency hiding; e g if you really need boost for a part of the class, you can have that in a separate header and implementation file, and a user of the class that needs the boost parts can import that header together with the one originally defining the class, and only that file will take ages to compile. This is more useful in the 64-bit runtime, where a category can add instance variables.

A really large implementation over several source files (but a single header) is also a good candidate, as Tom points out :)

I'd just like to add to Tom's original answer: Generally, it's better to use a class extension than a class category when declaring private methods. This way, you can implement the extension in the same @implementation block as the public methods, without getting a warning about "missing category implementation". Example:

// .h file

@interface Foo : NSObject
-(void)publicMethod;
@end

// .m file

@interface Foo () 
// Notice the empty paren; this is how you define
// a class extension, which is not the same as a category
-(void)somePrivateMethod;
@end

@implementation Foo
#pragma mark Public methods
-(void)publicMethod;
{ ... }

#pragma mark Private methods
-(void)privateMethod;
{ ... }
@end
Joachim Bengtsson