Understanding Objective-C Protocols: Definition, Usage, and Design Patterns
What is a Protocol?
A protocol in Objective-C serves a similar purpose to interfaces in Java. It defines a contract of methods that a class must implement, without providing any implementation details or member variables.
Key Characteristics of Protocols
- Protocols exist solely to declare collections of methods—they cannot contain instance variables or implementations.
- Any class conforming to a protocol must implemant all required methods declared in that protocol.
- When a parent class conforms to a protocol, all its subclasses automatically conform to that protocol as well.
- Protocols enable any class to adopt specific method signatures, acting as formal agreements between classes.
- Objective-C supports single inheritance only, but a class can conform to multiple protocols simultaneously. Use the colon (
:) for inheritance and angle brackets (<>) for protocol conformance. - The base protocol
<NSObject>declares fundamental methods that most objects need, such asdescriptionandretain. - Protocols can conform to other protocols, thereby inheriting all method declarations from the protocols they adopt.
Implementing Protocols
The typical workflow for using protocols involves three steps: defining the protocol, adopting it in a class interface, and implementing the required methods.
Defining a Protocol:
@protocol CommunicationProtocol <NSObject>
// Method declarations go here
@end
Note that protocols should adopt the <NSObject> protocol by convention.
Adopting a Protocol in a Class:
For a class to conform to a protocol:
@interface Employee : NSObject <CommunicationProtocol>
@end
For a protocol to conform to other protocols:
@protocol AdvancedProtocol <BasicProtocol, SecondaryProtocol>
@end
Separate multiple protocol names with commas in side the angle brackets.
Required vs Optional Methods
Two keywords control whether implementing a method is mandatory or optional:
@required: Methods marked as required must be implemented. Failure to implement them results in compiler warnings.@optional: Methods marked as optional can be implemented at the developer's discretion. The compiler will not warn if they're not implemented.
The default modifier is @required. These keywords primarily serve as communication tools between developers rather than enforcing strict runtime behavior, since Objective-C uses dynamic typing.
@protocol MovementProtocol <NSObject>
@required
- (void)walk;
- (void)jump;
@optional
- (void)sprint;
- (void)swim;
@end
Type Restrictions with Protocols
Protocols enable type checking beyond simple class inheritance.
Restricting id Variables:
@property (nonatomic, strong) id <DataProcessor> handler;
// The assigned object must conform to DataProcessor
Restricting Class-Type Variables:
NSObject <Serializable> *dataWrapper;
// Only NSObject subclasses conforming to Serializable are allowed
This pattern ensures that the compiler validates protocol conformance at compile time.
Delegate Design Pattern
The delegate pattern is a fundamental design approach where one object acts on behalf of or coordinates with another object.
Implementation Steps:
- Define a protocol containing methods that the delegate must implement.
- Create delegate classes that conform to this protocol.
- In the delegating clas, declare a property of type
idconforming to the delegate protocol. - Call delegate methods from within the delegating class when appropriate events occur.
- Assign an actual delegate instance to the delegating object from external code.
Example Protocol Definition:
@protocol TaskDelegate <NSObject>
- (void)didCompleteTask:(NSString *)taskName;
- (void)didFailTask:(NSString *)taskName withError:(NSError *)error;
@optional
- (void)taskWillBegin:(NSString *)taskName;
@end
Delegating Class Interface:
@interface TaskManager : NSObject
@property (nonatomic, weak) id <TaskDelegate> delegate;
- (void)startProcessing;
@end
Delegate Implementation Call:
@implementation TaskManager
- (void)startProcessing {
if ([self.delegate respondsToSelector:@selector(taskWillBegin:)]) {
[self.delegate taskWillBegin:@"DataSync"];
}
// Processing logic...
[self.delegate didCompleteTask:@"DataSync"];
}
@end
Common Use Cases for Delegates:
- Event notification and listener scenarios
- Data source implementations where one object provides data to another
- Situations where responsibilities should be delegated to external handlers rather than handled internally
Forward Declarations
Just as @class declares a class name without importing its full definition, protocols can use forward declarations to avoid circular dependencies.
In Header File (.h):
@protocol PaymentProcessor;
@interface Checkout : NSObject
@property (nonatomic, strong) id <PaymentProcessor> paymentHandler;
@end
// Compiler may show a warning about missing protocol definition
In Implementation File (.m):
#import "PaymentProcessor.h"
@implementation Checkout
// Implement methods here
@end
Alternatively, import the complete protocol definition directly in the header file if the protocol definition is available.