Notifications

Modified

Downloads

Notification - complete example.

Resources

Chapter 14 of Cocoa Programming text.

Overview

The MVC discussion introduced programmer defined notifications to illustrate the mechanism.

Notification is part of the API, simple and intuitive to use; once the notification concept is understood.

The key concept is that of a notification center that handles:

  1. adding observers
     
  2. notifying observers
     
  3. removing observers

 

 

Example

The following example illustrates sending a "Hello World" string through a notification.

  1. NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; the notification object.
     
  2. [nc addObserver: model1
          selector: @selector(input:)
          name: @"BUTTONCHANGE"
          object: nil];

    Adds model1 object as an observer of @"BUTTONCHANGE".

    Select that all @"BUTTONCHANGE" notifications sent to input: Model1 method.
     

  3. [nc postNotificationName: @"BUTTONCHANGE"
          object: @"Hello World"];


    Notify all observers of @"BUTTONCHANGE".

    Send to message selected (i.e. input:) @"Hello World".
     
  4. (void) input: (NSNotification *) notification {
       NSString * title = (NSString *)[ notification object ];

Receive notification object.

Cast as string.

HelloWorld.m
model1 = [Model1 new];

NSNotificationCenter *nc =
      [NSNotificationCenter defaultCenter];

[nc addObserver: model1
      selector: @selector(input:)
      name: @"BUTTONCHANGE"
      object: nil];

[nc postNotificationName: @"BUTTONCHANGE"
          object: @"Hello World"];

[nc removeObserver: model1];
 
Model1.m
-(void) input: (NSNotification *) notification {

   NSString * title = (NSString *)[ notification object ];

   NSLog(@" %@ ", title );
}

 

 

Example

Another calculator implementation. This demonstrates the use of notifications to further decouple components.

The key idea is for only the controller to have any knowledge of the other components, the Model1, Model2, and View. Data passes between each exclusively through notifications.

You may recall the MVC example in which Model1 held a reference to a View object, theDisplay, and Model2 to theCount. This coupled the Model-View-Controller more closely than necessary (i.e. the models had to know about UITextBox'es) and created a convoluted MVC relationship (upper-right).

The example above illustrated the ViewController notifying Model1 of a @"BUTTONCHANGE".

This example adds Model1 and Model2 notifying ViewController, decoupling the reference to a UITextBox object, producing the typical MVC relationship depicted lower-right.

Three different notifications are used:

  1. @"BUTTONCHANGE" to notify Model1 and Model2 objects of a button change and the button pressed.
     
  2. @"ACCUMULATORCHANGE" to notify ViewController object of an accumulator change and its value.
     
  3. @"COUNTCHANGE" to notify ViewController object of a count change and its value.

 

CalculatorAppViewController.m
#import "CalculatorAppViewController.h"
@implementation CalculatorAppViewController

- (void)viewDidLoad {
   [super viewDidLoad];

   model1 = [Model1 new];
   model2 = [Model2 new];

   NSNotificationCenter *nc =
              [NSNotificationCenter defaultCenter];
   [nc addObserver: model1
         selector: @selector(input:)
         name: @"BUTTONCHANGE"
         object: nil];

   [nc addObserver: model2
         selector: @selector(input:)
         name: @"BUTTONCHANGE"
         object: nil];

   [nc addObserver: self
         selector: @selector(count:)
         name: @"COUNTCHANGE"
         object: nil];

   [nc addObserver: self
         selector: @selector(accumulator:)
         name: @"ACCUMULATORCHANGE"
         object: nil];
}


-(IBAction) input: (id) sender {
   UIButton *uiButton = (UIButton *) sender;
   NSString *title = uiButton.currentTitle;

   NSNotificationCenter *nc =
         [NSNotificationCenter defaultCenter];

   [nc postNotificationName: @"BUTTONCHANGE"
          object: title];
}

-(void) accumulator: (NSNotification *) notification {
   theDisplay.text = (NSString *) [notification object];
}

-(void) count: (NSNotification *) notification {
   theCount.text = (NSString *) [notification object];
}

- (void)dealloc {
   NSNotificationCenter *nc =
           [NSNotificationCenter defaultCenter];
   [nc removeObserver: model1];
   [nc removeObserver: model2];
   [nc removeObserver: self];

   [model1 release];
   [model2 release];
   [super dealloc];
}
@end

Model1.m
#import "Model1.h"
@implementation Model1

-(void) input: (NSNotification *) notification {
   NSString * title = (NSString *)[ notification object ];

   char c = [title characterAtIndex: (NSInteger) 0];
   switch (c) {
      case '=' : [self equal]; break;
      case '+' : case '-' : case '*': case '/' :
         operation = c;
         operand = accumulator;
         accumulator = 0.0;
         break;
      case 'R' : operation = c; accumulator = 0; operand = '\0';
                     break;
      default : accumulator = accumulator * 10+(int)c-48;
   }
   NSString *s =
         [[NSString alloc] initWithFormat: @"%f", accumulator];


   [[NSNotificationCenter defaultCenter]
      postNotificationName:@"ACCUMULATORCHANGE"
      object: s];

   [s autorelease];
}

-(void) equal {
   switch (operation) {
      case '+' : accumulator = operand + accumulator; break;
      case '-' : accumulator = operand - accumulator; break;
      case '*' : accumulator = operand * accumulator; break;
      case '/' : accumulator = operand / accumulator; break;
   }
}

-(id) init {
   [super init];
   accumulator=0.0;
   operand='\0';
   return self;
}
@end

Model2.m
#import "Model2.h"

@implementation Model2

-(id) init {
   [super init];
   count = 0;
   return self;
}

-(void) input: (NSNotification *) title {
   count++;
   NSString *s =
        [[NSString alloc] initWithFormat: @"%3.0f", (double)count];
   [[NSNotificationCenter defaultCenter]
         postNotificationName:@"COUNTCHANGE" object: s];
   [s autorelease];
}

@end
CalculatorAppViewController.h
#import <UIKit/UIKit.h>
#import "Model1.h"
#import "Model2.h"

@interface CalculatorAppViewController : UIViewController {
   IBOutlet UITextField *theDisplay;
   IBOutlet UITextField *theCount;
   Model1 *model1;
   Model2 *model2;
}

-(IBAction) input: (id) sender;
@end
 
Model2.h
#import <Foundation/Foundation.h>

@interface Model2 : NSObject {
   int count;
}

-(void) input: (NSNotification *) title;
-(id) init;
@end
Model1.h
#import <Foundation/Foundation.h>

@interface Model1 : NSObject {
   double accumulator;
   double operand;
   char operation;
}

-(void) input: (NSNotification *)notification;
-(id) init;
-(void) equal;
@end

Notify versus direct method invocation

There is no single criteria for using notifications but there is always overhead.

Notify provides at least two advantages over direct method invocation, explored above:

  1. Decoupling of components.
  2. Sending message to an arbitrary number of objects.

Consider a music app that could do any or all of:

  1. play musical piece
  2. display artist info
  3. display musical score
  4. search for same piece by others

The user could select which of the four they wanted. Each selected could be added as an observer to be notified on a change of musical piece.

Obviously, either notification or direct invocation could be used. The issue is the degree of coupling and generality of the components.