Object-C Categories
When I read about Object-C Categories the first time I did not really understand what was good for. But as usual I need to actually use it in order to understand it but at least I had an idea where I could use it.
So what is it good for. Well, let’s have a look at the problem I faced. For the Minibloq project I used a quite elaborate cell and because there is a lot of code that needs to be handled within the cell I created a View Controller for the content of the cell. This is not too difficult to do until I had to code the scenario when a cell is reused. Then I need to get a hold of the View Controller in order to adjust its settings. Unfortunately there is no reference on the cell and so I would need to store that in the table’s view controller but that is not very elegant and use too much code. So what if we could add a new property to the cell that would contain a reference to my view controller so that when I get the cell back I can just ask for the view controller and then use it there. That is exactly what the Categories are good for.
If you are familiar with Aspect Oriented Programming a Category is what in AOP is called a Mixin which in other word is an dynamic extension of a class. That makes it possible to change or extend a class where we might not have the source code available or cannot change the code ourselves.
Create a new Category in XCode 4 ¶
1. Select “New File…
2. Select the “Object-C Category” from the iOS section
3. Specify the Class this Category is based on
4. Specify the Name of the Category
Attention: the name of the category is per convention:
Code the Category ¶
So far I created a category that will extend the UITableViewCell and allows me to add functionality to the base class. So I could create a property containing a reference for the View Controller and so we are done. But not so fast was what XCode 4 was telling me. It turns out that I cannot synthesize a property or use a property inside a Category and so I had to use some magic.
The header looks like this meaning there is nothing special about the header:
#import <UIKit/UIKit.h> @interface UITableViewCell (UITableViewCell_ViewController) @property (nonatomic, retain) UIViewController* viewController; @end
Looking into iOS I found that I could use its runtime environment to store a property:
#import "UITableViewCell+ViewController.h" #import <objc/runtime.h> static char STRING_KEY; @implementation UITableViewCell (UITableViewCell_ViewController) - (UIViewController*) viewController { return (UIViewController *) objc_getAssociatedObject( self, &STRING_KEY ); } - (void) setViewController:(UIViewController *)aViewController { objc_setAssociatedObject( self, &STRING_KEY, aViewController, OBJC_ASSOCIATION_RETAIN_NONATOMIC ); } - (void) dealloc { objc_setAssociatedObject( self, &STRING_KEY, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC ); [super dealloc]; } @end
Finally we can use it. The only think we have to make sure of is that we import the Category wherever we use it.:
#import "UITableViewCell+ViewController.h"
Use the Category ¶
Then we handle a Table Cell as if it is could reference a view controller:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell; NSString *cellIdentifier = @"ProgramCellView"; cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if( !cell ) { [[NSBundle mainBundle] loadNibNamed:cellIdentifier owner:self options:nil]; cell = myTableCell; InstructionViewController* instructionVC = [[InstructionViewController alloc] init]; [cell addSubview:instructionVC.view]; // When we create a new Cell we need to set the View Controller as well cell.viewController = instructionVC; [instructionVC release]; } // ... // Now we can obtain a reference to its view controller whenever we need it. InstructionViewController *instructionVC = (InstructionViewController *) cell.viewController; // ... return cell; }

