CodeToLive

Protocols & Delegation in Objective-C

Protocols define interfaces that can be implemented by any class, while delegation is a design pattern that uses protocols to enable communication between objects.

Protocols declare methods that can be implemented by any class, similar to interfaces in other languages. Delegation is a design pattern where one object acts on behalf of another.

Declaring Protocols


// NetworkFetcher.h
@protocol NetworkFetcherDelegate <NSObject>

@required
- (void)fetcher:(id)fetcher didReceiveData:(NSData *)data;

@optional
- (void)fetcher:(id)fetcher didFailWithError:(NSError *)error;
- (void)fetcherDidStart:(id)fetcher;
- (void)fetcherDidFinish:(id)fetcher;

@end

@interface NetworkFetcher : NSObject

@property (weak, nonatomic) id<NetworkFetcherDelegate> delegate;

- (void)startFetching;

@end
            

Note: The <NSObject> syntax means the protocol conforms to the NSObject protocol, which is common practice for most protocols.

Implementing Protocols


// ViewController.h
#import "NetworkFetcher.h"

@interface ViewController : UIViewController <NetworkFetcherDelegate>
@end

// ViewController.m
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NetworkFetcher *fetcher = [[NetworkFetcher alloc] init];
    fetcher.delegate = self;
    [fetcher startFetching];
}

#pragma mark - NetworkFetcherDelegate

- (void)fetcher:(id)fetcher didReceiveData:(NSData *)data {
    // Handle received data
    NSLog(@"Received %lu bytes", (unsigned long)data.length);
}

- (void)fetcher:(id)fetcher didFailWithError:(NSError *)error {
    // Handle error (optional)
    NSLog(@"Error: %@", error.localizedDescription);
}

@end
            

Checking Protocol Conformance


if ([self.delegate conformsToProtocol:@protocol(NetworkFetcherDelegate)]) {
    if ([self.delegate respondsToSelector:@selector(fetcherDidStart:)]) {
        [self.delegate fetcherDidStart:self];
    }
}
            

Protocol Inheritance


@protocol Scrollable <NSObject>
- (void)scrollToTop;
@end

@protocol Refreshable <Scrollable>
- (void)refreshData;
@end

// A class adopting Refreshable must implement:
// - refreshData
// - scrollToTop
// - NSObject protocol methods
            

Delegation Pattern

Delegation Pattern Components

  1. Delegate Protocol: Defines the methods the delegate should implement
  2. Delegate Property: A weak reference to the delegate object
  3. Delegate Implementation: The object that implements the protocol methods

// TableViewManager.h
@protocol TableViewManagerDelegate <NSObject>

- (NSInteger)numberOfRows;
- (UITableViewCell *)cellForRowAtIndex:(NSInteger)index;
@optional
- (void)didSelectRowAtIndex:(NSInteger)index;

@end

@interface TableViewManager : NSObject

@property (weak, nonatomic) id<TableViewManagerDelegate> delegate;

- (void)reloadData;

@end
            

Multiple Protocol Conformance


@interface MyViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UIScrollViewDelegate>

@end
            

Protocols as Types


// Can use protocols as parameter and return types
- (id<Drawable>)createDrawableShape;
- (void)renderShape:(id<Drawable>)shape;

// Property with protocol type
@property (strong, nonatomic) id<Parser> dataParser;
            

Common Cocoa Delegation Patterns

Class Protocol Purpose
UITableView UITableViewDelegate, UITableViewDataSource Table view management
UICollectionView UICollectionViewDelegate, UICollectionViewDataSource Collection view management
UITextField UITextFieldDelegate Text field events
UIApplication UIApplicationDelegate App lifecycle events
CLLocationManager CLLocationManagerDelegate Location updates

Blocks vs Delegation

Criteria Delegation Blocks
Complexity Better for complex, multi-method interfaces Better for simple, one-off callbacks
Memory Weak references prevent retain cycles Need careful memory management
Readability Methods grouped in protocol Inline logic can be harder to follow
Flexibility Single delegate Multiple handlers possible

Best Practices

Warning: Forgetting to set the delegate property or not implementing required methods are common sources of bugs. Always double-check these when debugging.

Advanced: Informal Protocols


// Before formal protocols existed, categories on NSObject were used
@interface NSObject (MyInformalProtocol)
- (void)informalMethod;
@end

// Implementation would check with respondsToSelector:
if ([object respondsToSelector:@selector(informalMethod)]) {
    [object informalMethod];
}
            

Note: Informal protocols are largely obsolete now that formal protocols support optional methods.

Real-World Example: Custom Delegation


// PrinterManager.h
@protocol PrinterManagerDelegate <NSObject>

- (void)printerManager:(PrinterManager *)manager didFinishPrinting:(BOOL)success;
- (NSInteger)numberOfPagesToPrint;
@optional
- (void)printerManagerDidStart:(PrinterManager *)manager;

@end

@interface PrinterManager : NSObject

@property (weak, nonatomic) id<PrinterManagerDelegate> delegate;

- (void)startPrinting;

@end

// PrinterManager.m
@implementation PrinterManager

- (void)startPrinting {
    if ([self.delegate respondsToSelector:@selector(printerManagerDidStart:)]) {
        [self.delegate printerManagerDidStart:self];
    }
    
    NSInteger pages = [self.delegate numberOfPagesToPrint];
    BOOL success = [self printPages:pages];
    
    [self.delegate printerManager:self didFinishPrinting:success];
}

@end
            
Next: Foundation Framework in Objective-C