Welkin's Secret Garden

Observer Pattern for Cocoa(1)

The Observer Pattern is a design pattern which defines an One-To-Many relation, letting multiple objects called observer observe another object called subject at the same time. When the state of the subject changes, all the observers will be notified.

Here shows the structure:

There are two roles in this pattern as I described above. When an object wants to observe a subject, the subject will maintain the observer, thus the subject maintains a list of observers, which may easily cause memory leak . Observers should also expose the methods for subject to invoke whenever its state changes.

What does it look like in Cocoa?

Certainly, simply following the structure chart and writing down all the classes and methods is the easiest way to implement observer pattern, but we have other choices. In Cocoa, there are three implementations of observer pattern naturally.

  1. Delegation
  2. Key-Value observing
  3. NSNotification

So let’s begin from delegation . I would like to show the declaration of delegation in Apple’s documentation.

Delegation is a simple and powerful pattern in which one object in a program acts on behalf of, or in coordination with, another object. The delegating object keeps a reference to the other object—the delegate—and at the appropriate time sends a message to it.
—— developer.apple.com

From the documentation, we learn that the model of delegation should be used in One-To-One situation which means there should be only one observer that observes the subject. However, just for practice, I will show what it looks like with delegation.

Let’s begin with Tom and Jerry

Suppose that one day Jerry wants to find something to eat while Tom is sleeping. Be careful, Jerry must run away before Tom wakes up. That means Jerry ought to be notified when Tom is ready to wake up. Thus we know the relation of them:

Observer Subject
Jerry Tom

According to the structure chart of observer pattern, all observers shall expose the same method so as to be invoked by the subject. Therefore, we first define a protocol .

1
2
3
4
5
@protocol TomDelegate <NSObject>

- (void)tomWillWakeUp;

@end

And Tom maintains an observer, that is, who conforms to TomDelegate .

1
2
3
4
5
6
7
@interface Tom : NSObject

@property (weak, nonatomic) id<TomDelegate> delegate;

- (void)wakeUp;

@end

It is very simple. If Jerry wants to be notified, It needs to conform to TomDelegate , implementing the tomWillWakeUp method.

1
2
3
@interface Jerry : NSObject <TomDelegate>

@end

When Tom is ready to wake up, Jerry runs away immediately.

1
2
3
4
5
6
7
- (void)runAway {
NSLog(@"Jerry %@ run away.", self);
}

- (void)tomWillWakeUp {
[self runAway];
}

Ok! Let’s play!

1
2
3
4
Jerry *jerry = [[Jerry alloc] init];
Tom *tom = [[Tom alloc] init];
tom.delegate = jerry;
[tom wakeUp];

Result:

1
2
Jerry <Jerry: 0x608000012c90> run away.
Tom <Tom: 0x608000012c50> is awake.

Jerry escaped!

I would like to show a whole picture of it.

Instead of super class, Cocoa prefers to define a protocol because any class can conform to it regardless of its super class.

Someone may find that there is only one observer in this case. So what if there are several Jerrys? We got to know that delegation is for One-To-One just now, but we can just make a little change to make it adapted to more Jerrys.

Change delegate to delegates :

1
2
3
4
5
@interface Tom : NSObject

@property (strong, nonatomic) NSHashTable<id<TomDelegate>> *delegates;

@end

Then modify the wakeUp method:

1
2
3
4
5
6
7
8
- (void)wakeUp {
for (id<TomDelegate> delegate in self.delegates) {
if ([delegate respondsToSelector:@selector(tomWillWakeUp)]) {
[delegate tomWillWakeUp];
}
}
NSLog(@"Tom %@ is awake.", self);
}

That’s ok but you should pay attention to delegates because it’s very easy to lead to a retain cycle . The delegates container should hold weak references with delegates. [NSHashTable weakObjectsHashTable] can give you a hand on this occasion. However, there must be other choices for us in One-To-Many .

To be continue.