tags:

views:

115

answers:

3

Hello! Do you have some tricks or techniques to break Cocoa controller classes into smaller chunks? I find that whatever I do the controllers end up being one of the more complicated classes in my design. The basic stuff is simple, but once I have several pop-overs or action sheets running, things get uncomfortably complex. It's not that bad, but still I would like to refactor the code into several standalone parts.

I thought about categories, but the code is not that independent (a lot of times it needs to tap into viewWillAppear, for example) and I find that I spend a long time fighting the compiler. I also thought about adding functionality in layers using inheritance, but that feels like a hack.

+3  A: 

Categories is the way to go. The trick (for your "not that independent" stuff) is to declare the category methods in your main controller .h file (not a separate controller+category.h file, there will be none) then implement them in your controller+category.m file. Like this:

//******************
// MyController.h
//******************
#import <Cocoa/Cocoa.h>

@interface MyContoller : NSObject 
{
    NSWindow   *window;

    // stuff to implement in the category
    NSComboBox *someCombo;
    NSButton       *someButton; 
}

@property IBOutlet NSWindow   *window;

@property IBOutlet NSComboBox *someCombo;
@property IBOutlet NSButton   *someButton;

@end

@interface  MyController (MyCategory)

- (IBAction)someComboSelected:(id)sender;
- (IBAction)someButtonPressed:(id)sender;

@end

//**************************
// MyController+MyCategory.m
//**************************
#import <Cocoa/Cocoa.h>
#import "MyController.h"

@implementation MyContoller (MyCategory)

- (IBAction)someComboSelected:(id)sender
{
    ....
}

- (IBAction)someButtonPressed:(id)sender
{
    ....
}

Obviously, I didn't include "MyController.m", where you put the "@synthesize" stuff and whatever else is needed for the main controller/awake-from-nib/whatever. Anyway, doing it this way gives your controller methods access to the category methods and vise-versa, and the category methods have access to all the properties.

obijohn
+3  A: 

The issue is not size, but responsibility. Is your controller wearing more than one hat? If so, blow it up into multiple, one-job-per-class controllers.

Categories help with size, but not responsibility. If you're still doing multiple things in one (amalgamated) class, then you still have a problem; moving them into separate files did not solve it.

Having many categories on a single class brings a risk of method collisions: implementing the same method in multiple categories, probably by implementing it in category B while forgetting that category A already has one. This will cause a problem when the object sends itself a message, expecting one category's response to that message and getting the other's.

Declaring all of the categories in the main class header mitigates that risk, as you can see that another category already has a method by the name you're about to enter. However, every method you add, thereby lengthening the header file, mitigates the mitigation.

If your controller is wearing more than one hat, blow it up into multiple classes.

I recommend Martin Fowler's book “Refactoring”. Refactoring your code is cleaning it up, and blowing up too-big classes (and methods and functions) is a subset of such clean-up.

Of course, multiple classes that once were one need a replacement for the communication that was previously internal within the class. Cocoa provides a number of solutions:

You don't need to pick exactly one, nor do you need to use them all. Which solutions are appropriate will depend on exactly what communication needs your new classes will have with each other.

Peter Hosey
Blocks made my life much easier, because I can remove the delegation methods like those required by pop-overs and action sheets (for example using http://github.com/zoul/Lambda-Alert). Can’t wait for blocks to appear on iPad, I would not have said they make such a difference.
zoul
+2  A: 

In regular Cocoa, NSViewController can be used to manage controller complexity by allowing "components" within a window to have their own overall controllers.

You may be able to do something similar with UIViewController, or you may want to create "coordinating controllers" outside your view/view controller hierarchy to manage your model objects' behaviors separately.

Chris Hanson