Delegates and Protocols

Delegates and protocols are very useful concepts in object-oriented programming. These concepts are often used together, especially when a way to provide notifications to unknown objects is needed.

Lets assume that we have a couple of classes that need to be notified when an event occurs. On this event occurring, they need to be notified with a result but they need to have their own implementation that may not be the same in different classes.

First thing to do is to create a protocol, let’s call it MyProtocol… which will be used to define the notification method. For example, we would like to get an int value to all of our delegate objects.

@protocol 

- (void) didReceiveOperationParameter:(int)theIntValue;

@end

Now we are creating a class called OperationsClass that will inform the delegates with a result.

OperationsClass.h:

#import "Protocol.h"

@interface OperationsClass : NSObject 
{
	
	id < MyProtocol > delegate;
	NSMutableArray *arrayOfDelegates;

}


- (void) registerAsObserver:(id)aDelegate;
- (void) notifyAllObservers;


@end

OperationsClass.m

#import "OperationsClass.h"


@implementation OperationsClass

- (id) init
{
	self = [super init];
	if (self != nil) 
	{
		arrayOfDelegates = [[NSMutableArray alloc] init];
	}
	return self;
}


- (void) registerAsObserver:(id)aDelegate
{
	if ( ![arrayOfDelegates containsObject:aDelegate] )
		[arrayOfDelegates addObject:aDelegate];
}


- (void) notifyAllObservers
{
	for ( id < MyProtocol > aDelegate in arrayOfDelegates ) 
	{
		[aDelegate didReceiveOperationParameter:10];
	}
}


@end

We can see that the class is initialized with an array of observers. These observers are delegates registered with this class. This means that they are added to the array of delegates and they will be notified on an event, when the notifyAllObservers method is called. The use of protocols is very important because, in this case, the OperationsClass does not need to know the kind of the delegate classes. The only thing important is that the delegate class implements MyProtocol. If a class implements MyProtocols it means it will respond to the method declared in the protocol, and that method can be called.

Let’s create two delegate classes that would implement the same protocol, but the implementation would be different for each class. The first one is called DelegateClass, and the other is called AnotherDelegateClass.

DelegateClass.h:

#import "Protocol.h"
#import "OperationsClass.h"


@interface DelegateClass : NSObject  < MyProtocol > {

}


- (id) initWithOperationInstance:(OperationsClass *)theOperationInstance;


@end

DelegateClass.m:

#import "DelegateClass.h"
#import "OperationsClass.h"


@implementation DelegateClass


- (id) initWithOperationInstance:(OperationsClass *)theOperationInstance
{
	self = [super init];
	if (self != nil) 
	{
		[theOperationInstance registerAsObserver:self];
	}
	return self;
}



- (void) didReceiveOperationParameter:(int)theIntValue
{
	NSLog(@"Value = %d", theIntValue);
}


@end

DelegateClass implements the protocol MyProtocol and registers itself as observer with OperationsClass on initialization. You can see the implementation of protocol method didReceiveOperationParameter.

The other delegate class is called AnotherDelegateClass.

AnotherDelegateClass.h:

#import "Protocol.h"
#import "OperationsClass.h"


@interface AnotherDelegateClass : NSObject < MyProtocol > {

}


- (id) initWithOperationInstance:(OperationsClass *)theOperationInstance;


@end

AnotherDelegateClass.m:

#import "AnotherDelegateClass.h"


@implementation AnotherDelegateClass


- (id) initWithOperationInstance:(OperationsClass *)theOperationInstance
{
	self = [super init];
	if (self != nil) 
	{
		[theOperationInstance registerAsObserver:self];
	}
	return self;
}


- (void) didReceiveOperationParameter:(int)theIntValue
{
	int someValue = theIntValue + 15;
	
	NSLog(@"Completely different implementation: someValue = %d", someValue);
}


@end

AnotherDelegateClass implements the protocol MyProtocol and registers itself as observer with OperationsClass on initialization, same as the DelegateClass. You can see that the implementation of the protocol method didReceiveOperationParameter is different than the implementation of the same method in the DelegateClass.

Let’s implement the code above. First, an instance of the OperationsClass is created.

OperationsClass *operationInstance = [[OperationsClass alloc] init];

Next, the DelegateClass instance is created and initialized with the operation instance. In the constructor, the delegateClassInstance is registered as an observer.

DelegateClass *delegateClassInstance = [[DelegateClass alloc] initWithOperationInstance:operationInstance];

Let’s try to notify the observers and let’s see what we get.

NSLog(@"First notification (only one observer in the list)");
[operationInstance notifyAllObservers];
NSLog(@"====================================================");
	
Output:

First notification (only one observer in the list)
Value = 10
====================================================

OperationsClass instance notifies all observers in the array, at the moment there is only one observer – delegateClassInstance.
Let’s create another delegate class instance and notify all observers.

AnotherDelegateClass *anotherDelegateInstance = [[AnotherDelegateClass alloc] initWithOperationInstance:operationInstance];
	
NSLog(@"Second notification (both observers in the list)");
[operationInstance notifyAllObservers];
NSLog(@"====================================================");

Output:
	
Second notification (both observers in the list)
Value = 10
Completely different implementation: someValue = 25
====================================================

In the output, we can see that both delegates are notified since they are registered as observers in operationsInstance. They are both notified with the same method and parameter, but the implementation of that method is completely different in each class.

The delegation pattern is very commonly used in object-oriented programming. When the class of the delegate object is not know at the time of development, protocols come in hand. The class in question is not important if it implements a known protocol.