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)?
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
Which approach minimizes lines of code?
Which approach causes the least amount of coupling and semantic coupling?
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];
}
}
The model can be referenced by multiple controllers. For a good insight on the basics of model-view-controller in iPhone development, scan through the first 2 lectures of the iPhone development course at Stanford (available for free in iTunesU, see info at Stanford at http://www.stanford.edu/class/cs193p/cgi-bin/drupal/ ) There seem to be more ways to get controllers informed about view and/or model updates.
I'm not sure why you're stuck on a Singleton, and I also don't see the problem in making a Singleton model object. I think it you also need to consider thread safety and memory leaks.
Related
I'm new to Objective-C and the iPhone and thought I was getting the hang of it until, after many play apps, I ran into a really basic problem around MVCs, NIBs and IB. Are there any really clear explained examples of how to follow this framework that I could go back to?
#interface test1ViewController : UIViewController {
IBOutlet myView *myview;
IBOutlet myModel *mymodel;
}
Both the views and models are linked in by IBOutlets but instantiating the model object either kills the application or produces an object which does not respond to any messages.
I am also unclear as to where to carry best out initialisations. I currently do this over viewDidLoad in the view controller. Is there a standard way to do this and does the simulator actually always start up in the same way? Should one use awakeFromNib? Does it make any difference if I use plain code or the IB? And if I have use the IB, should it include a model object?
The MVC idea would make good sense for me here because I have potentially several views and view controllers all feeding back into - and sharing - one common central data model. Any references or advance on this newbie problem would be more than welcome!
I wouldn't spend too much time worrying about the 'classic' definition of MVC. iOS follows it, but there's a lot of confusing terminology. ("View Controller")
You say trying to use model kills your app. Are you retaining myModel? You have to retain all IBOutlets.
nibs are collections of "Freeze-Dried" objects. When you load a nib, the objects in it are "rehydrated", if you will. This means they spring back to life with all of their properties set to whatever they were when you froze them. So you talk of "instantiating" and "initializing" but this does not apply to IB. The objects are ALREADY instantiated and initialized. Imagine that compiling the nib 'pauses' the objects. When you load the nib, the objects resume doing whatever they were doing when frozen. They will get an awakeFromNib message, so that's a good place to put some code to check on what the state of the app is, see if you have to do stuff with your object to bring it up to speed.
viewDidLoad seems like an "initialization" method but don't be fooled. It's part of the view controller life cycle and it can be called more than once! (If your controller's view is purged as part of a low memory warning, viewDidLoad might be called again if the view has to be... wait for it... reloaded.) So, it's appropriate to put view setup stuff in viewDidLoad, but not other initialization type things.
For the "common data" thing, I like to create a singleton data model class. Your various views can set properties on the model, or send notifications. You can also use KVO (key value observing) to watch for changes in the model.
IB makes functionality invisible. I don't like it and I don't use IB any more, preferring to have everything in code. Then when you look at code you see what is going on - all the navigation controllers, all the formatters etc. - without switching over to IB. Maybe Xcode4 will make it better with integrated IB but I probably won't go back. Lots of people do like IB so try both methods and see what you like best.
IBOutlet/IBAction actually mean nothing to the compiler but they let IB recognise the ivars it can send messages to or that will write to elements in a xib. Your use of it here is a bit simplistic unless you really do have a model that only communicates one way with the xib. What you more usually have is a range of controls in the xib linked to the view object, the view communicating directly with the controller. The controller communicates with the model. Very loosely speaking, model is your internal representation of data and logic, view is what you see, controller is the glue between them.
The MVC line can be fuzzy and you just have to get comfortable with it. If you have a slider control with a value representing some value in your model then it can be hard to think of it as part of the interface especially when you persist the value and use it as a central part of your model. I like the Stanford iPhone class introduction of it. They don't spend a heap of time on it because it can be difficult to follow exactly and there are situations where it isn't best.
Notes from that class - you can find the video on iTunes to follow along.
Your use of viewDidLoad is correct, that's your chance to perform initialization on views and their objects. If using IB you will probably not have much to do there because you can set most properties in the xib. If not using IB you will use it a lot more.
A lot of times something like your model would be wired at runtime by your application delegate or by the view controller itself.
IB is generally used more to link views and controllers together, with the application handing around model(s).
That said, you should be able to have IB create an instance of your model and assign it in an IBOutlet. Was your model OK with just being created without the classic init method being called? Did it implement NSCoding properly?
I have a common application pattern: user enters data in the main view controller, then views it in a table in a modal view controller, where rows can be deleted or modified. I was following the general design strategy from the Stanford iPhone course, but somewhere things went off the rails and all I've been getting is SIGABRT's and exceptions like "Illegal attempt to establish a relationship 'xyz' between objects in different contexts."
As in the Stanford course, I use a singleton class called "Database" which should return the same context whenever requested. So the first commands in my viewDidLoad method on the main view controller are:
dbsingleton = [Database sharedInstance];
nmocontext = [dbsingleton managedObjectContext];
nmocontext is an ivar I use throughout the view controller. When the user wants to see the other view controller, with the table, I alloc-init it, then present it modally. (It has an NSFetchedResultsController that supplies the data from my store.) I've tried various strategies here:
I've made the NSFetchedResultsController a retained property set by the main view controller
I've made the NSManagedObjectContext a retained property set by the main view controller; and
I've used the singleton internally by repeating those two lines of code above at the beginning of the table view controller's viewDidLoad method.
Whichever I go with, the one problem I just can't solve is that after the user closes and deallocs the table view controller (and its NSFetchedResultsController), I start getting crashes in the main view controller when the store is accessed (like the "Illegal attempt" error mentioned above).
What are best practices for dealing with this common application pattern? I am still hoping to make this application iPhone SDK 3.x compatible, but I do seem to have fewer crashes when I'm using iOS 4 -- if there are underlying issues with 3.x that are causing me problems, please let me know and I may target iOS 4 only.
Thank you!
This is just a guess but I assume the following problem for your crashes after closing the tableview:
You declared a property
#property (retain, nonatomic) NSManagedObjectContext* nmocontext;
do you properly release the ivar nmocontext in dealloc?
If yes your problem is the assignment
nmocontext = [dbsingleton managedObjectContext];
This never retains nmocontext within your viewcontroller but you release it on dealloc.
Second:
"Illegal attempt to establish a relationship 'xyz' between objects in different contexts."
This is not a memory management issue, but you probably created another new context to add objects (like in the apple core data iphone samples) and tried to set a NSManagedObject as relationship from a different context.
It sounds like you don't have your singleton properly configured.
A singleton should override release to do nothing such that when it is sent a release message nothing happens. If you do not override release then any random piece of code anywhere in the app can kill the singleton and defeat the entire purpose of using a singleton. The next time you call singleton, you actually get another new object which in this case also returns a new managed object context.
See Cocoa Fundamentals Guide: Creating a Singleton Instance.
Singletons are powerful and flexible but very easy to do wrong. They are so easy to screw up that many experienced developers simply refuse to use them. If don't have experience with them, don't use them when you are just starting out.
Im building an app that will use a Core Data model. I pretty new at Objective C and my usual design patterns does not really apply on Core Data and Objective C, at least I can't seem to find examples that confirms they will.
I have been through the Apple Developer examples and different sources on the intertubes.
It seems that to leverage Core Data I need to pass the managedObjectContext to each of my viewControllers, have the viewController implement the NSFetchedResultsControllerDelegate and then implement each of the methods for doing a fetch and subsequently implement
NSFetchedResultsChangeInsert
NSFetchedResultsChangeDelete
NSFetchedResultsChangeMove
NSFetchedResultsChangeUpdate
This adds approximately 100+ lines of code in each viewController and it is 90% the same code I write again and again. Plus I have to pass everything around and keep track of it's memory footprint.
In other languages I would build a singleton model of a few classes that held methods for maintaining and delivering data upon request, available from anywhere. It seems I can't take that approach in Objective C. If I where to build a static Class that took a managedObjectContext and returned me what I needed, I would still have to pass the managedObjectContext around to every view and it wouldn't be asynchronously like when I implement delegate methods that just gets called when a result is ready.
I hope this makes sense and that someone can either confirm that there is no other reasonable way to do it or help point me in a direction for wrapping this up in a good way.
Thanks:)
Core Data is not nearly as complicated as you describe.
Generally, an iPhone app has a "main" managed object context, which is generally owned by the app delegate. So long as you can get the app delegate (hint: [[UIApplication sharedApplication] delegate]) you have access to the managed object context. I like to define a static global variable to hold a reference to my app delegate to make life easier.
There's generally a one-to-one correspondence between NSFetchedResultsController instances and UITableView instances. Aside from populating table views, it's exceedingly rare that you would need an NSFetchedResultsController. If you have a number of similar views (e.g. a tab bar that lets you view the same data in different ways a la the iPod app), it would behoove you to create a single base class that configures the NSFetchedResultsController and derive your specific view controllers from that.
Now, when you create view controllers to edit an object, it's generally a good idea to do that in a separate managed object context. If the user cancels, you just discard the context and the changes go away. Again, you don't really need an NSFetchedResultsController for this because these views are only concerned with a single object.
When you're done editing, you save: the managed object context. The objects that manage your other managed object contexts should implement the NSFetchedResultsControllerDelegate methods to keep the table view in sync. Again, this can be implemented in a base class so you can generalize this functionality for related view controllers.
Do you absolutely have to use a CoreData model, or would something using a NSCoder (NSArchiver, NSKeyedArchiver, etc) work? I've found that CoreData is overkill for most applications.
Also, could you clarify why you can't take an approach using singletons? I've used singleton factories in a number of applications without issues. It's fairly easy to define class-level methods that operate on a shared (singleton) instance.
In the iPhone Core Data Template, Apple places the Core Data Stack in the App Delegate.
My initial inclination however is to move this code into it's own class whose responsibility is to handle the management of the Core Data Stack.
Do you typically encapsulate this functionality within its own class or do you leave it in the App Delegate?
Summary: There is no need to create a singleton to manage the Core Data stack; indeed doing so is likely to be counter-productive.
The Core Data stack happens to be created by the application delegate. Importantly, however, as all the examples show, the stack (principally the managed object context) is not retrieved directly from the stack(*). Instead the context is passed to the first view controller, and from them on a context or a managed object is passed from one view controller to the next (as described in Accessing the Core Data Stack). This follows the basic pattern for iPhone all applications: you pass data or a model controller from one view controller to the next.
The typical role of the singleton as described here is as a model controller. With Core Data, the managed object context is already a model controller. It also gives you the ability to access other parts of the stack if needs be. Moreover, in some situations (as described in the documentation) you might want to use a different context to perform a discrete set of actions. The appropriate unit of currency for a view controller is therefore usually a managed object context, otherwise a managed object. Using and passing a singleton object that manages a stack (and from which you retrieve a context) typically at best introduces a needless level of indirection, and at worst introduces unnecessary application rigidity.
(*) No example retrieves the context using:
[[UIApplication delegate] managedObjectContext];
I have a singleton class that i let do my core data managment and i do not leave it on the app delegate. I rather not clutter the app delegate class with methods i might need for conviniece such as fetching certain objects etc
I leave the core data logic in the App delegate for the following reasons:
1) I do not see any real advantage in moving this code in other classes: the concept of delegation is perfectly fulfilled by the core data logic being handled by the App delegate since the core data model is actually a fundamental part of your application;
2) In all of the sample code I have seen, including Apple samples, the core data stuff is handled by the App delegate;
3) Even in Core Data books it is common practice to have the App delegate handle core data related code;
4) Personally I do not think that readability or anything else is actually improved by having ad hoc classes for core data, but this is a matter of personal taste and I will not argue here what approach is the best one. To me, simplicity while retaining functionality is important.
The question I'd ask myself, in your case, is "who does the Core Data stack 'belong' to?" The data itself is really province of the application, isn't it? (C.F. Core Data on the Mac, where you might have an application capable of working with multiple documents at a time, so the Core Data stack belongs to each document.)
In any Cocoa/Cocoa Touch application, the App Delegate is usually the preferred means of customizing the behavior of the application, so this is the natural place for the Core Data stack.
Now, the problem I suspect you're having is that it feels wrong to constantly write things like:
NSManagedObjectContext *context = [(MyAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
What I typically do in these cases is write functions (not methods) like this:
NSManagedObjectContext *UIAppManagedObjectContext() {
return [*(MyAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
I write a similar function for the NSPersistentStoreCoordinator and the NSManagedObjectModel. I put all of these in the App Delegate's .h/.m files, since these are application-level objects, too.
I'll just list this in a new answer. (I've scrapped my previous FJSCoreDataStack class in favor of this)
My new way of handling this has been to use a category on NSManagedObjectContext. Ive added the following class methods:
+ (NSManagedObjectContext *)defaultManagedObjectContext;
+ (NSManagedObjectContext *)scratchpadManagedObjectContext;
+ (NSManagedObjectModel *)managedObjectModel;
+ (NSPersistentStoreCoordinator *)persistentStoreCoordinator;
+ (NSString *)applicationDocumentsDirectory;
This keeps everything out of my app delegate, and gives singleton access should I choose to use it. However, I still use dependency injection from the App Delegate (as mmalc has said, it introduces inflexibility into my code). I have simply moved all of the "Core Data Stack" code into the NSManagedObjectCOntext Category.
I like passing the reference around, especially since I have a nice "scratchpad context" method. This keeps my View Controllers flexible since I have not committed them to the "defaultManagedObjectContext".
Also relevant to the conversation in the iPhone world (and may have a bearing on your architecture):
NSFetchedResultsController and constructing NSFetchRequests
I'm in favour of having the app delegate know where the model starts, and having the model know where the Managed Object Context is. The Core Data-"ness" of the model seems like an implementation detail of the model to me, the controller classes (like the app delegate) should just ask "give me this information about the model" and the model should know how to answer that question. Therefore having a Core Data object available through a controller object seems like a leaky abstraction.
I am developing an iPhone app for some sweet undergrad research I've been working on. Sadly, my school doesn't offer software engineering / design classes so when it comes to questions of best practices in OO Design, I do a lot of reading.
My Dilemma:
My application loads a view (v1) where, upon user's button click, v1's controller class executes an action method. This action method should fill an array with objects. After that, the user will either execute the action again or click a different tab to load another view. Other views in the application will use the array that v1 populated.
So, where should this shared array be declared? Right now, it's in the AppDelegate class from when I was testing features without a GUI. Should I grab the AppDelegate singleton and add items to it in the v1ViewController? Should it be declared as static?
Thanks for the help!
^Buffalo
EDIT:
Follow-up Question: When interacting with a singleton, which is the better way to talk to it:
[[MyAwesomeSingleton sharedInstance] gimmeSomePizza];
or
MySingleton *s = [MySingleton sharedInstance];
[s gimmeSomePizza];
I guess what I'm wondering is, do you make the sharedInstance method call every time or do you define a pointer to the sharedInstance and then reference the pointer?
Using the app delegate to store data that's shared between views and view controllers is reasonable and appropriate.
In my apps, I view the app delegate as the controller part of MVC, with UIViews and view controllers all being part of the "view". I prefer to use a variant of MVC called Passive View that keeps the model and view parts of my app strictly segregated with only the controller connecting them.
I'm assuming that the array of objects you're storing is your app's model, so storing them on your app delegate makes sense. As Daniel D said, there's no need to make it static.
The app delegate is really the heart of your program. You create and initialize your model and views in your -applicationDidFinishLaunching: method and save your model data and view state in -applicationWillTerminate:. When your view controllers receive events that changes your model, you can call methods on your app delegate to make those changes.
You could store it in an ivar in the app delegate. You don't need to make it static since the app delegate is a singleton anyways (there's never more than 1 instance).
If the app delegate is getting a bit complicated, you can factor out the data storage into a separate model object or perhaps use Core Data.