MVC
Model View Controller

Modified

Downloads

Calculator command line example.

Calculator iPhone example.

Download

Open the Downloads in Finder.

Double-click MVCCalculatorApp.xcodeproj

In Xcode click Build and Run

Resources

Delegates - http://en.wikipedia.org/wiki/Delegation_pattern

Overview

MVC is considered by some an architecture and others a programming pattern. Either defines clear responsibilities, communication and loose coupling between the three primary components of most user systems.

The figure above illustrates a common communication relationship where the Controller connects the View and Model.

The three components of MVC have the following, typical responsibilities:

Controller: sends input from View to Model, sends Model state to View.

Model: maintains state of the application.

View: display of Model state.

Example

The following example implements a calculator using MVC pattern.

First, examining the @interface files helps to understand the MVC connections.

Interaction between the MVC components is managed by the MyController implementation.

MyController.h
@interface MyController : NSObject {
        MyModel *myModel;
        MyView *myView;
}
-(id) initModel: (MyModel *) model
              view: (MyView *) view;
-(void) run;   
@end
MyModel.h
@interface MyModel : NSObject {	
	double accumulator;
	double opr;
	char op;
}

-(void) setModel: (char) c;
-(id) init;
-(NSString *) getModel;
@end
MyView.h
@interface MyView : NSObject {}
    -(void) setView: (NSString *) s;
@end
 

 

MyController.m
 @implementation MyController
-(id) initModel: (MyModel *) model view: (MyView *) view {
    myModel = model;
    myView = view;
    return self;
}

-(void) run {
    char c='\0';

    while( c!='Q' ) {	
       scanf( "%c", &c );
       if(c != '\n'  && c != '\r' && c != 'Q') {
            [myModel setModel: c];
            [myView setView: [ myModel getModel ]];
       }
    }
}
@end
MyView.m
@implementation MyView

-(void) setView: (NSString *) s {
     printf("%s\n", [s cStringUsingEncoding:NSUTF8StringEncoding]);
}
@end
 
main.m
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

   MyModel *myModel = [MyModel new];
    MyView *myView = [MyView new];
    MyController *myController = 
          [[MyController alloc] initModel: myModel view: myView];
    [myController run];

    [myController release];
    [myModel release];
    [myView release];
    [pool drain];
    return 0;
}
MyModel.m
@implementation MyModel

-(void) setModel: (char) c { 
    switch (c) {
        case 'C':
            opr = 0.0;
            accumulator= 0.0;
            break;
        case '+':; case '-':; case '*':; case '/' : 
            op = c; 
            opr = accumulator;
            accumulator = 0.0;
            break;
        case '=' :
            switch (op) {
                case '+':
                    accumulator = opr + accumulator;
                    break;
                case '-':
                    accumulator = opr - accumulator;
                    break;
                case '*':
                    accumulator = opr * accumulator;
                    break;
                case '/':
                    accumulator = opr / accumulator;
                    break;
                default:
                    break;
            }
            break;
        default:			// assume '0'..'9' digit
            accumulator = accumulator * 10.0 + (c-'0');
            break;
    }
}

-(NSString *) getModel {
    return [NSString stringWithFormat:@"%f", accumulator];
}

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

Note that input and output is in the Target Output window

or the Console.

Execution:

Input:

3+4=

Output:

3.000000
0.000000
4.000000
7.000000

iOS Calculator

Apple development tools are designed around the Model-View-Controller (MVC). You may have noticed that iOS apps have a ViewController appended to the app name.

View is constructed by Interface Builder (IB) for the iOS, the View is the objects of buttons, text boxes, etc.

Model is program code that maintains the state of the application.

Controller (i.e. ViewController) is connected by IB to the View through outlets and references to View objects. The diagram illustrates the central role of the Controller as passing messages between the Model and View.

For an iPhone calculator:

Controller: receives and sends button input to modify calculator Model state, and sends Model state to View.

Model: maintains state of the application, the accumulator, etc.

View: display of Model state, literally a display, created using Interface Builder.

MVC communication should be loosely coupled to promote reuse.

Controller notifies Model and notifies View of Model state change.

Ideally, Controller, Model and View are independent, no a priori knowledge of others.

Not exactly the case here, since the ViewController calls Model methods directly.

 

Example

The following example implements a calculator using MVC in the typical iPhone-style.

The File's Owner panel of Interface Builder shows that the interface buttons have all been connected to a single Controller run: method that receives the action Touch Up Inside and the button that sent the message.

MVCCalculatorAppViewController.h
#import <UIKit/UIKit.h>
#import "MyModel.h"

@interface MVCCalculatorAppViewController : UIViewController {
     MyModel *myModel;
     IBOutlet UITextField *myView;
}

-(IBAction) run: (id) sender;

@end
 
MyModel.h
#import <Foundation/Foundation.h>

@interface MyModel : NSObject {	
	double accumulator;
	double opr;
	char op;
}
@property (nonatomic, assign) double accumulator;

-(void) setModel: (char) c;
-(id) init;
-(NSString *) getModel;
@end
MVCCalculatorAppViewController.m
#import "MVCCalculatorAppViewController.h"

@implementation MVCCalculatorAppViewController

- (void) viewDidLoad {
     [super viewDidLoad];
     myModel = [[MyModel alloc] init];
}
 
-(IBAction) run: (id) sender {
   UIButton *uiButton = (UIButton *) sender;
   NSString *title = uiButton.currentTitle;

   [myModel setModel: [title characterAtIndex: (NSInteger) 0]];

   myView.text = [myModel getModel];
}

-(void) dealloc {
   [myModel release];
   [super dealloc];
}
@end

 

Note the grayed code is where the ViewController sends:

  1. the character from the button title to the Model
  2. the Model results to the View.

MyModel.m
#import "MyModel.h"

@implementation MyModel

-(void) setModel: (char) c { 
    switch (c) {
        case 'C':
            opr = 0.0;
            accumulator= 0.0;
            break;
        case '+':; case '-':; case '*':; case '/' : 
            op = c; 
            opr = accumulator;
            accumulator = 0.0;
            break;
        case '=' :
            switch (op) {
                case '+':
                    accumulator = opr + accumulator;
                    break;
                case '-':
                    accumulator = opr - accumulator;
                    break;
                case '*':
                    accumulator = opr * accumulator;
                    break;
                case '/':
                    accumulator = opr / accumulator;
                    break;
                default:
                    break;
            }
            break;
        default:			// assume '0'..'9' digit
            accumulator = accumulator * 10.0 + (c-'0');
            break;
    }
}

-(NSString *) getModel {
    return [NSString stringWithFormat:@"%f", accumulator];
}

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

@property AND @synthesize

Suppose we wanted setters and getters for the accumulator of MyModel. We could write our own.

getter
-(double) accumulator {
    return accumulator;
}
double x = [myModel accumulator];

setter
-(void) accumulator: (double) a {
    accumulator = a;
}
[myModel accumulator: 5.0];
      

Setters and getters can be created automatically.

@property defines instance variable properties such as:

readwrite - whether to create getter (read) and setter (write) methods automatically.

nonatomic - getter/setter does not need locks for thread safe operation.

assign - the default, setter assigns a value to variable.

copy - setter creates a new instance that is a copy of the variable.

 @synthesize specifies the instance variables to generate getter/setter automatically.

@interface MyModel : NSObject {	
	double accumulator;
}
@property (nonatomic, assign, readwrite) double accumulator;
@implementation MyModel

@synthesize accumulator;

Creates the getter and setter for accumulator automatically that define two forms of syntax.

getter
double x = [myModel accumulator];
                 or
double x = myModel.accumulator;

setter
[myModel accumulator: 5.0];
               or
myModel.accumulator = 5.0; 

 


PROTOCOLS

The MVCCalculatorAppViewController works but uses an ad hoc protocol for communicating with the MyModel; by setter method calls to -setModel: and getter method -getModel of MyModel.

Recall that the MVCCalculatorAppViewController notifies the MyModel when new data (button presses) arrive, through -setModel: messages.

A formal protocol defines the interface to methods that must be implemented.

Listener.h defines a protocol Listener and the method -setModel: (char) that must be implemented.

MyModel.h inherits NSObject and promises to implement the -setModel: (char) method of the Listener protocol.

MVCCalculatorAppViewController.h defines a MyModel object that implements the -setModel: (char) method.

MyModel.h
#import <Foundation/Foundation.h>
#import "Listener.h"

@interface MyModel : NSObject <Listener> {

   double accumulator;
   double operand;
   char operation;
}

-(void) setModel: (char) c;
-(id) init;
-(NSString *) getModel;
@end
Listener.h
@protocol Listener
 
-(void) setModel: (char)c;

@end
MVCCalculatorAppViewController.h
#import <UIKit/UIKit.h>
#import "MyModel.h"

@interface MVCCalculatorAppViewController : UIViewController {
     MyModel <Listener> *myModel;
     IBOutlet UITextField *myView;
}

-(IBAction) run: (id) sender;

@end

 

At this point there seems to be little gained by using a protocol but we will see that protocols assist in designing loosely coupled components.

 

DELEGATES

In the example above, a MyModel object is a delegate by virtue of implementing the Listener protocol, it can receive a setModel: message.

The following sends an setModel: 'X' message to a MyModel object.

MyModel <Listener> *myModel = [MyModel alloc];

[myModel input: 'X'];

This appears no different than the usual method invocation.

The key difference is, as a Listener protocol delegate, MyModel is required to implement setModel: which allows  setModel: messages to be sent to a MyModel object.

Delegates are used extensively in iOS development to send messages on events.

For example, the following view controller is a UITextFieldDelegate delegate and implements the textFieldShouldReturn: message, sent when the return key is tapped on the iPhone keyboard.

[textField resignFirstResponder]; dismisses the keyboard.

@interface SomeViewController : UIViewController <UITextFieldDelegate> {
     IBOutlet IUTextField *text;
 }
@end
-(id) init {
        [super init];
        [text setDelegate: self];
        return self];
}
-(BOOL) textFieldShouldReturn: (UITextField *) textField {
   NSLog(@"Text field contents %@ ", textField.text );
   [textField resignFirstResponder];
   return YES;
}

-init method initializes the UITextField text delegate programmatically by:

[text setDelegate: self];

where self is the SomeViewController object.

Now, when the keyboard is no longer needed for typing in the text object, the UITextField class calls textFieldShouldReturn: method of SomeViewController.

The SomeViewController can also connected as a delegate to the text field object using Interface Builder.

The mechanism used here to send delegate messages is beyond our needs for discussion (see Resources) at this point but will be examined more completely later.

For now we examine another messaging mechanism below.

NOTIFYING LISTENERS

Consider the case where the MVCCalculatorAppViewController has several different Models to manage: a model for the calculations and a model for counting the number of button presses.

In this case, the calculation model could be extended to do both but that obfuscates the purpose of the calculation Model.

Generally, loosely coupled components that implement a single, clear operation are more effective in building large systems than a single large, tightly coupled system. But you know all about reuse, information hiding, etc.

Ideally, a loosely coupled set of components could be wired together as long as each followed a common communication protocol.

Think of wiring different components to build a home entertainment system. Each component follows a protocol for input and output, and can literally be wired together, if components have a connection point that follows a specified protocol.

Listener protocol defines such a connection point:

-(void) setModel: (char)c; 

and Listener objects conform to the Listener protocol (define a connection point) by implementing:

 -(void) setModel: (char)c;

Listener.h
@protocol Listener
 
-(void) setModel: (char)c;

@end

 

Model1.h
#import <Foundation/Foundation.h>
#import "Listener.h"

@interface Model : NSObject <Listener> {

   double accumulator;
   double operand;
   char operation;
}

-(void) setModel: (char) c;
-(id) init;
-(NSString *) getModel;
@end

In the diagram at right, the ViewController:

Model1 and Model2 each:

The following implements a listener pattern consisting of 3 parts:

  1. ManageListeners
    • adds a listener object to a listener list
    • notify listener objects on that list
       
  2. Listener
    • Model1 - listener that maintains calculator state
    • Model2 - listener that maintains count state
       
  3. ViewController
    • constructs the Model1 and Model2 objects
    • uses ManageListeners to add the Model1 and Model2 objects to the listener list
    • uses ManageListeners to notify the Model1 and Model2 objects of new data

The key execution steps are:

  1. ViewController constructs Model1 and Model2 Listener objects.

      Model1 *model1 = [Model1 alloc] ;
      Model2 *model2 = [Model2 alloc];
     

  2. ViewController adds Model1 and Model2 objects to the Listener list (in ManageListeners), effectively wiring the ViewController to the Model1 and Model2 objects.

      [theManageListeners addListener: model1];
      [theManageListeners addListener: model2];
     

  3. ViewController notifies Listeners (Model1 and Model2) objects when a button action is received through the wired Listener protocol connection.

    [theManageListeners notifyListeners: c];

Listener

MVCCalculatorAppViewController.h
#import <UIKit/UIKit.h>
#import "Model1.h"
#import "Model2.h"
#import "ManageListeners.h"

@interface MVCCalculatorAppViewController :
                         UIViewController {
  ManageListeners * theManageListeners;
  IBOutlet UITextField *myView;
  IBOutlet UITextField *theCount;
}

-(IBAction) run: (id) sender;

@end
 
Listener.h
@protocol Listener
 
-(void) setModel: (char)c;

@end
MVCCalculatorAppViewController.m
#import "MVCCalculatorAppViewController.h"

@implementation MVCCalculatorAppViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    theManageListeners = [ManageListeners new];

    Model1 *model1 = [Model1 alloc];
    Model2 *model2 = [Model2 alloc];

    [theManageListeners addListener: model1];
    [theManageListeners addListener: model2];

    [model1 release];
    [model2 release];
}

-(IBAction) run: (id) sender {
   UIButton *uiButton = (UIButton *) sender;
   NSString *title = uiButton.currentTitle;
   char c = [title characterAtIndex: (NSInteger) 0];

   [theManageListeners notifyListeners: c];
}
@end

 
ManageListeners.h
#import <Foundation/Foundation.h>
#import "Listener.h"

@interface ManageListeners : NSObject {
    NSMutableArray * listeners;
}

-(id) init;
-(void) addListener: (NSObject <Listener> *) listener;
-(void) notifyListeners: (char) c;

@end

Note that ManageListeners functions could be
implemented in ViewController.
 

ManageListeners.m
#import "ManageListeners.h"

@implementation ManageListeners

-(void) addListener: (NSObject <Listener>*) listener {
   [listeners addObject: listener];
}

-(void) notifyListeners: (char) c {
   for( NSObject <Listener> *listener in listeners)
        [listener setModel: c];
}

-(id) init {
   [super init];
   listeners = [NSMutableArray new];
   return self;
}
@end
Model2.h
#import <Foundation/Foundation.h>
#import "Listener.h"

@interface Model2 : NSObject <Listener> {
   int count=0;
}

-(void) setModel: (char) c;
-(NSString *) getModel;
@end
Model2.m
#import "Model2.h"
@implementation Model2

-(void) setModel: (char) c {
   count++;
}

-(NSString *) getModel {
   return [NSString stringWithFormat:@"%d", count];
}
@end

Model1.h
#import <Foundation/Foundation.h>
#import "Listener.h"

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

-(void) setModel: (char) c;
-(NSString *) getModel;
@end

 

Model1.m
#import "MyModel1.h"

@implementation MyModel1

-(void) setModel: (char) c {
    switch (c) {
        case 'C':
            opr = 0.0;
            accumulator= 0.0;
            break;
        case '+':; case '-':; case '*':; case '/' :
            op = c;
            opr = accumulator;
            accumulator = 0.0;
            break;
        case '=' :
            switch (op) {
                case '+':
                    accumulator = opr + accumulator;
                    break;
                case '-':
                    accumulator = opr - accumulator;
                    break;
                case '*':
                    accumulator = opr * accumulator;
                    break;
                case '/':
                    accumulator = opr / accumulator;
                    break;
                default:
                    break;
            }
            break;
        default: // assume '0'..'9' digit
            accumulator = accumulator * 10.0 + (c-'0');
            break;
    }
}

-(NSString *) getModel {
    return [NSString stringWithFormat:@"%f", accumulator];
}

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

MVCCalculatorAppViewController with models updated by Model1 and Model2.

 

See Notifications for Objective-C/Apple approach.