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
- Delegate Protocol: Defines the methods the delegate should implement
- Delegate Property: A weak reference to the delegate object
- 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
- Always declare delegate properties as
weak
to avoid retain cycles - Use the
respondsToSelector:
check for optional methods - Name delegate methods clearly indicating the sender:
- Good:
tableView:didSelectRowAtIndexPath:
- Bad:
didSelectRow:
- Good:
- Group related protocol methods with
#pragma mark
- Consider using blocks for simple, one-time callbacks
- Document whether your protocol methods are called on the main thread
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