Protocols and Delegation
|
This section documents modern Objective-C as implemented by Apple Clang in the current Xcode release — Objective-C 2.0 plus ARC, literals and subscripting, This content was generated with the assistance of AI and should be verified against developer.apple.com before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
A protocol is a named list of method declarations with no implementation — Objective-C’s interface type. Any class may adopt any number of protocols, which is how the language provides the "implement several contracts" capability that single inheritance alone cannot. Protocols underpin delegation, the pattern Cocoa uses almost everywhere in place of subclassing.
Declaring a Protocol
// MyDownloaderDelegate.h
@class MyDownloader;
@protocol MyDownloaderDelegate <NSObject>
@required
- (void)downloader:(MyDownloader *)downloader didFinishWithData:(NSData *)data;
@optional
- (void)downloader:(MyDownloader *)downloader didUpdateProgress:(double)progress;
- (void)downloaderDidCancel:(MyDownloader *)downloader;
@end
@required methods must be implemented by any adopter — the compiler warns if one is missing.
@optional methods may be absent, so the sender must check before calling. Without either directive,
declarations are @required.
The Cocoa naming convention for delegate methods is worth following exactly: the first argument is the sender,
and the selector reads as a sentence about what happened (didFinish…, willBegin…, should…). It makes
delegate code self-documenting at the call site.
Adopting and Conforming
A class adopts protocols by listing them in angle brackets:
@interface ViewController : UIViewController <MyDownloaderDelegate, NSCoding>
@end
Adopt privately — in the class extension — whenever conformance is an implementation detail rather than part of the class’s public contract:
// ViewController.m
@interface ViewController () <MyDownloaderDelegate>
@end
id<Protocol> Typing
The most useful thing about a protocol is as a type: "any object at all, provided it can do these things".
@property (nonatomic, weak) id<MyDownloaderDelegate> delegate;
- (void)setHandler:(id<MyDownloaderDelegate>)handler;
// A concrete class plus a protocol, when you need both:
@property (nonatomic, strong) UIView<MyHighlightable> *highlightedView;
id<MyDownloaderDelegate> is checked at compile time: sending a message not in the protocol (and not in
NSObject) is a warning. That makes it strictly better than a bare id for this purpose. And it is better
than a concrete class type, because it lets any class — including a test double — satisfy the requirement.
A protocol object itself is obtained with @protocol(…):
Protocol *p = @protocol(MyDownloaderDelegate);
NSString *name = NSStringFromProtocol(p); // @"MyDownloaderDelegate"
Protocol Inheritance and the NSObject Protocol
A protocol may extend others, inheriting all their requirements:
@protocol MyReadable <NSObject>
- (NSData *)readData;
@end
@protocol MyReadWritable <MyReadable> // requires readData too
- (void)writeData:(NSData *)data;
@end
Almost every protocol should inherit from NSObject — note this is the NSObject protocol, not the
class. Foundation declares both with the same name; <NSObject> in a protocol list means the protocol. It
declares the fundamental messages (isEqual:, hash, description, respondsToSelector:, retain,
release), and without it the compiler will not let you send even respondsToSelector: to your
id<MyProtocol> — which is precisely what the optional-method idiom requires.
@protocol Bad // does not inherit <NSObject>
- (void)doSomething;
@end
id<Bad> obj = …;
// [obj respondsToSelector:@selector(doSomething)]; // warning: not in the protocol
Run-Time Conformance Checks
// Does this object claim the whole protocol?
if ([object conformsToProtocol:@protocol(MyDownloaderDelegate)]) { … }
// Does it implement this particular (optional) method?
if ([object respondsToSelector:@selector(downloader:didUpdateProgress:)]) { … }
// Class-level query:
if ([SomeClass conformsToProtocol:@protocol(NSCopying)]) { … }
The distinction matters: conformsToProtocol: answers "was this protocol declared in the adoption list",
which says nothing about whether the @optional methods were actually implemented. For calling an optional
method, respondsToSelector: is the only correct check.
Prefer either of these to isKindOfClass: when what you need is a capability — it is more flexible, survives
refactoring, and works with proxies and class clusters.
The Delegate Pattern
Delegation lets one object hand off decisions or notifications to another, without either knowing the other’s class. It is the single most common design pattern in Cocoa.
// MyDownloader.h
@interface MyDownloader : NSObject
@property (nonatomic, weak) id<MyDownloaderDelegate> delegate; // weak! see below
- (void)start;
@end
// MyDownloader.m
@implementation MyDownloader
- (void)finishWithData:(NSData *)data {
// A @required method: the delegate may be nil, but a message to nil is a no-op,
// so no guard is needed.
[self.delegate downloader:self didFinishWithData:data];
}
- (void)reportProgress:(double)progress {
// An @optional method: respondsToSelector: is mandatory here.
if ([self.delegate respondsToSelector:@selector(downloader:didUpdateProgress:)]) {
[self.delegate downloader:self didUpdateProgress:progress];
}
}
@end
And on the other side:
// ViewController.m
@interface ViewController () <MyDownloaderDelegate>
@property (nonatomic, strong) MyDownloader *downloader; // strong: the controller owns it
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.downloader = [[MyDownloader alloc] init];
self.downloader.delegate = self; // the back-reference is weak
[self.downloader start];
}
- (void)downloader:(MyDownloader *)downloader didFinishWithData:(NSData *)data {
[self renderData:data];
}
@end
The Callback Sequence
(the delegate) participant D as MyDownloader participant N as Network VC->>D: alloc/init VC->>D: downloader.delegate = self
(weak reference back to VC) VC->>D: start D->>N: begin request N-->>D: bytes arrive D->>D: respondsToSelector:
@selector(downloader:didUpdateProgress:) alt delegate implements the optional method D-->>VC: downloader:didUpdateProgress: VC->>VC: update the progress bar else not implemented D->>D: skip the call end N-->>D: transfer complete D-->>VC: downloader:didFinishWithData:
(@required -- called unconditionally) VC->>VC: render the data Note over VC,D: VC holds D strongly, D holds VC weakly.
If both were strong, neither would ever be deallocated.
Why Delegates Are weak
The owner holds the worker strongly; the worker must therefore hold the owner weakly. If both references were strong, the pair would form a retain cycle and neither object would ever be deallocated — a leak that ARC cannot detect or break.
@property (nonatomic, weak) id<MyDownloaderDelegate> delegate; // correct
// @property (nonatomic, strong) id<MyDownloaderDelegate> delegate; // leaks
weak brings a second benefit: the reference is zeroed automatically when the delegate is deallocated, so a
late callback goes to nil and is harmlessly ignored rather than crashing on a dangling pointer.
Two caveats:
-
A few classes need
assign/unsafe_unretainedinstead — some older Core Foundation-backed classes do not support weak references. These do dangle, so such a delegate must be cleared indealloc. -
A block-based callback is not automatically safe. A
copyblock property retains everything it captures, so capturingselfinside a completion handler creates exactly the cycleweakavoids here. See Blocks.
Retain cycles in general are covered in Automatic Reference Counting.
Delegate versus Data Source
Cocoa splits the pattern in two whenever both roles apply — UITableView has a delegate and a
dataSource:
| Data source | Delegate | |
|---|---|---|
Question answered |
"What should I display?" |
"Something happened — what now?" |
Direction |
Object pulls data from you |
Object pushes events to you |
Typical methods |
|
|
Usually |
Yes |
No |
Keeping them separate lets one object supply the content while another handles the interaction.
When Not to Delegate
Delegation is right for a one-to-one, ongoing relationship. Other shapes have better tools:
| Need | Use |
|---|---|
One-to-one, several related callbacks over time |
Delegate protocol |
A single "when you’re done" callback |
A completion block — keeps the caller’s code in one place |
One-to-many broadcast |
|
Loose coupling to a UI control |
Target-action |
See Blocks and Key-Value Coding and Observing.
See Also
-
Blocks — the completion-handler alternative, and its own retain-cycle rules.
-
Messaging and Selectors —
respondsToSelector:and messages tonil. -
Inheritance and Polymorphism — delegation as the alternative to subclassing.
-
Coding Conventions and Style — delegate method naming in full.