When building iOS apps in Objective-C, one of the biggest challenges is getting different objects to communicate without tightly linking them together.
A view controller may need updates from a network manager, or a table view may need to inform its controller that a user has scrolled or selected a cell. Writing direct references for every such interaction quickly leads to messy, hard-to-manage code. This is where the Objective-C delegate pattern comes in.
What is Objective-C Delegate?
Delegation is one of the most common and practical uses of protocols in Objective-C. It’s a pattern that lets one object hand over certain responsibilities to another, while still maintaining a clear separation of roles. This approach keeps your code flexible and prevents classes from becoming overly dependent on each other.
A delegate is simply an object that conforms to a protocol and implements its methods. The main class defines the protocol and uses a delegate property to communicate updates or actions. Here’s an example written clean and simple:
@protocol DownloadDelegate <NSObject>
@required
- (void)didFinishDownloading:(NSData *)data;
@end
@interface Downloader : NSObject
@property (nonatomic, weak, nullable) id<DownloadDelegate> delegate;
- (void)startDownload;
@end
@implementation Downloader
- (void)startDownload {
NSLog(@"Downloading file...");
NSData *data = [NSData new];
[self.delegate didFinishDownloading:data];
}
@end
@interface FileHandler : NSObject <DownloadDelegate>
@end
@implementation FileHandler
- (void)didFinishDownloading:(NSData *)data {
NSLog(@"File download completed successfully with data length: %lu", (unsigned long)data.length);
}
@end
In this example, Downloader performs a task and notifies its delegate once it’s done. The FileHandler class adopts the DownloadDelegate protocol and implements the required method. The key here is the weak delegate property, which avoids memory retain cycles by not holding a strong reference to the delegate object.
Using protocols for delegation allows you to design reusable components. The class performing an operation doesn’t need to know what specific object it’s talking to, as long as that object conforms to the protocol. This approach keeps your code decoupled, making it much easier to test and extend.
If you ever need to verify whether an object actually supports a specific protocol, you can do so at runtime using:
if ([object conformsToProtocol:@protocol(DownloadDelegate)]) {
NSLog(@"This object can act as a delegate");
}
Wrapping Up
Delegates play a crucial role in keeping Objective-C projects organized and scalable. They allow you to design components that talk to each other without unnecessary dependencies, making your app architecture easier to manage as it grows.
As a best practice, always mark delegate properties as weak to avoid retain cycles. Keep delegate methods focused on a single responsibility, and prefix protocol names with the class or feature they belong to, such as TableViewDelegate or NetworkManagerDelegate.
Avoid placing too many unrelated methods into a single delegate, and remember that a good delegate relationship is one where both classes know only what they need to.