Benedict's Soapbox

Adding Delegate Methods for an Objective-C Subclass (Extending a Protocol)

Delegation is a very common design pattern in Cocoa, AppKit and UIKit. Delegation provides many of the benefits of subclassing but without the pain and hassle that subclassing often involves. When delegation cannot provide the desired results subclassing can still provide a solution. Of course, the subclass can still use delegation to aid reuse.

Delegate properties general have the form:

@property(nonatomic, assign) id <uiscrollviewdelegate>delegate;[/objc]</uiscrollviewdelegate>

UIScrollViewDelegate is a protocol that describes the delegate methods that the object supports. When a subclasses provides additional delegate methods the protocol type should be changed to include the additional methods. This is achieved by ‘subclassing’ the delegate protocol. Here’s how to do it:

1. Define a new protocol that incorporate the existing protocol and defines the new delegate methods:

@protocol UITableViewDelegate <UIScrollViewDelegate>  
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;  

(Apples delegate protocols incorporate the NSObject protocol. The NSObject protocol defines respondsToSelector: which is used to determine if a delegate implements  method).

2. In the interface redefine the delegate property to use the new protocol:

@property(nonatomic, assign) id <UITableViewDelegate>delegate;

Redefining a superclasses property can be dangerous. If the type of the redefined property does not fulfil the requirements of the superclasses property then the superclass may attempt to perform invalid actions on the object. In this situation it is not a problem as we are redefining the property to be stricter than the superclass.

3. In the implementation tell the compiler that the delegate property will be provided at run time:

@dynamic delegate;

At runtime the property will be provided by the superclass.

That’s it! Some things to bare in mind when working with protocols:

if ((self.delegate respondsToSelector:@selector(tableView:willDisplayCell:forRowAtIndexPath:))  
    [self.delegate tableView:self willDisplayCell:cell forRowAtIndexPath:indexPath];