Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Model object ownership and MVC in Cocoa / iOS / iPhone

I'm trying to understand how to better implement the Model-View-Controller design pattern.

What object should 'own' the Model object? Should a single Controller instantiate (own) the Model object?

Here is an example scenario:

I have a UITabbarController containing two UIViewControllers (controllerA and controllerB). Obviously neither of these controllers own each other. I have a Model object which contains some data and also performs some network activity. Both controllerA and controllerB need to be able to make changes to the Model object. controllerB needs to know when changes have been made to the Model object (either by controllerA or returned results from the network activity). From recent reading:

  • I need KVO between the Model object and controllerB?
  • Should the Model object be a singleton? So that both controllers can modify it?
  • In simpler apps, I've had the viewController own the Model object. Is there any way for one controller to instantiate the Model object, but for other controllers to have write access to it?
  • I've also read about using the app delegate to own the Model object, and allowing controllers access via the app delegate share instance. This seems kind of ugly - using the app delegate singleton to globally access my Model object. Wouldn't it be better to just make my Model object a singleton?
  • I saw someone on SO give this link to iPhoneDevSDK, but the reasons for his method escape me. Again, isn't this getting the app delegate involved for something that should be just a singleton?

Mainly, is there any other way for two Controllers to access (write to) one Model, other than through the Model being a singleton?

Also, when a Controller owns another Controller (eg in a UINavigationController when the root view controller instantiates another view controller to stack on top of itself), would the best method for sharing the Model be for the root view controller to instantiate the Model, and pass it to the next view controller before pushing it onto the nav stack)?

like image 497
MattyG Avatar asked Jul 08 '11 22:07

MattyG


1 Answers

Storing global objects in the AppDelegate gets really ugly as your project scales.

Ask yourself: What is the relationship between this model object(s) and other objects in my application? Is the relationship 1-to-1 or 1-to-n. If you need just one model to be accessed by various objects, then go for the singleton approach. If you need one object to have exactly one model, then keep a pointer to it in that object.

When faced with different, yet computationally correct, design alternatives a few areas to consider

  1. Which approach minimizes lines of code?
  2. Which approach causes the least amount of coupling and semantic coupling?
  3. From the perspective of a programmer new to the project, which approach is easier to understand, maintain and harder to in-advertently introduce bugs.

If you start rolling global models into the AppDelegate, you'll eventually create a monolithic class which will be difficult to understand and harder to maintain. If you create a pointer to the model in each controller, you must pass a reference to that model each time a new control is instantiated, and it must pass down the pointer to any needing object which it instantiates. Essentially, you've created a cascading waterfall of passing the same pointer, bloated your interface file and constructor. Imagine if instead of 1 model you need to keep track of 5 model objects. Does it make sense for objects to have 5 pointers for 5 models which need to be passed-along to the constructor each and every time? This is how to make your projects buggy and unmaintainable.

In case it isn't obvious. The AppDelegate is just a singleton. When you roll all your models in your app delegate, you haven't avoided using singletons, you've just created a monolithic one.

Regarding KVO: This depends on what you're trying to accomplish. I'll give an example of where KVO is useful. Supposed you have a model object storing the application's user preferences.

@interface SettingsModel
.....
@property (nonatomic, retain) UIColor * backgroundColor;
@end

Other views in the application should update their background colour immediately whenever this settings changes. This can be solved easily using KVO:

[[SettingsModel getInstance] addObserver:self forKeyPath:@"backgroundColor" options:0 context:nil];


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
     if ([keyPath isEqualToString:@"backgroundColor"]){
         self.view.backgroundColor = [[SettingsModel getInstance] backgroundColor];
     }
}
like image 60
lorean Avatar answered Nov 04 '22 14:11

lorean