MVC
|
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.
|
|
|
|
|
||||
|
|
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.
|
|
|||
Note the grayed code is where the ViewController sends:
|
|
@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.
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;
@endIn the diagram at right, the ViewController:
- receives button messages,
- sends a message to Model1 and Model2
- sends a message to the View
Model1 and Model2 each:
- receive messages from ViewController,
The following implements a listener pattern consisting of 3 parts:
- ManageListeners
- adds a listener object to a listener list
- notify listener objects on that list
- Listener
- Model1 - listener that maintains calculator state
- Model2 - listener that maintains count state
- 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:
- ViewController constructs Model1 and Model2 Listener objects.
Model1 *model1 = [Model1 alloc] ;
Model2 *model2 = [Model2 alloc];
- 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];
ViewController notifies Listeners (Model1 and Model2) objects when a button action is received through the wired Listener protocol connection.
[theManageListeners notifyListeners: c];
|
Listener |
||||
|
|
|||
Note that ManageListeners functions could be |
|
|||
|
|
|||
|
|
|||
MVCCalculatorAppViewController with models updated by Model1 and Model2.
See Notifications for Objective-C/Apple approach.