All the examples I've come across so far ( including Stanford podcasts ) reference a Model by declaring it as a property of a View Controller and using it from there:
#import "myClass.h" // assume it carries a single NSString property
#interface
#property (nonatomic,strong) myClass *myobject;
#end
#implementation ViewController
-(void)viewDidLoad {
self.myObject = [[myClass alloc] init]
.
.
.
-(void)someMethod{
displayLabel = self.myObject.myString;
seems like that's more self.C-V than M-V-C.
After messin' about on my own this works:
#import "myClass.h"
#implementation ViewController {
MyClass *myObject;
}
-(void)viewDidLoad {
myObject = [[myClass alloc] init]
}
.
.
.
-(void)someMethod{
displayLabel = myObject.myString;
my question is; is there any danger in using second example? or to ask differently, does it give the compiler an easier task of keeping MODEL separate from VIEW and CONTROLLER?
There are a couple of implementation details that are different between your two examples but they are essentially doing the exact same thing.
In both cases you are declaring a backing ivar. This line #property (nonatomic,strong) myClass *myobject; will implicitly #synthesize myObject = _myObject;, which is similar to you manually writing:
#interface ViewController : UIViewController {
MyClass *_myObject;
}
// or
#implementation ViewController {
MyClass *_myObject;
}
The only other difference is that #property (nonatomic,strong) myClass *myobject; will also create the accessor methods for you
- (void)setMyObject:(MyClass *)myObject;
- (MyObject *)myObject;
This is indeed still MVC but controllers that are subclasses of UIViewController always manage at least one view. The M component is your myObject instance. As in most diagrams the Controller sits there managing the communications between the V and M both of which the controller owns
Using singletons or holding the model as a property of the controller is also a question of lifetime. I prefer using singletons for helper classes (getting data from game center or fetching data from core data). Singletons are useful for things you need to access from different places within your app (where lifetime of your singleton is longer than your view controller's lifetime). But for view controller dependent models you can of course use them as properties of the view controller. Let's say you have ten view controllers in your app each displaying completely different content it does absolutely not make sense to keep all data for all possible view controllers in memory (in a singleton) just to have data ready in case the user wants to see any of the view controllers. In this case it's no shame to load your model's data from within your view controller implementation and hold it as a property. This guarantees that data is auto released when the view controller's lifetime ends and avoids conflicts. Holding data in singleton would make sense when you display data loaded from a server that does not need to be refreshed everytime you present your data to reduce the amount of traffic generated by loading the data. Using singletons might be dangerous regarding thread safety e.g. when data is mutated from a background thread while iterating over your datasource object to refresh a table view's content. Singletons can also lead to tigh coupling which should be avoided. Using an instance variable instead of a property is still a good choice if you want to hold a weak reference to an object as it is automatically set to nil if the referenced object gets auto released. A weak property would in this case lead to bad access.
Suppose I have several objects (models) that belong to a UIViewController. When one of the models changes or does something, I want it to notify the UIViewController so that the view can update immediately, or perform some animation in response.
How do I do this? Specifically,
(1.) How do the individual objects know about the ViewController? If I store a pointer to the ViewController inside each object, then I introduce a cyclical reference where a ViewController refers to children that refer to ViewController.
(2.) Even with a weak pointer, the model objects themselves don't know which view inside the ViewController corresponds to itself. For example, say there are 10 views and 10 models. The models aren't coupled with the views (i.e. each model has a view that represents it in the array of 10, but the index isn't always the same, since the user is constantly switching them around, so while the models have a stable index 0...9, the view representing model 0 could be at index 1, or later at index 2, 3, ..., 9), so when a model updates, it needs to tell the ViewController somehow which of the 10 views represents it, except it doesn't know. So the ViewController in turn can't know which View to update.
I should add that my current code just gives each model object a pointer to its corresponding View, so that it doesn't need to go through the ViewController to access the correct View and update its contents. But this would seem to violate MVC, so I'm trying to correct my code and realized I don't know how in this particular situation.
Sorry if that is confusing. I'll update the question if anything is unclear.
EDIT: Updated with more concrete example code.
#interface ViewController : UIViewController<ProfileViewControllerDelegate>
{
BattleModel* battle;
}
#property (nonatomic, strong) BattleModel* battle;
#property (nonatomic, weak) NSArray* heroPersons; // Person objects
#property (nonatomic, weak) NSArray* villainPersons; // Person objects
#property (nonatomic, strong) IBOutletCollection(PersonImageView) NSArray* villainImages;
#property (nonatomic, strong) IBOutletCollection(HealthBarView) NSArray* villainHealthBars;
#property (nonatomic, strong) IBOutletCollection(PersonImageView) NSArray* heroImages;
#property (nonatomic, strong) IBOutletCollection(HealthBarView) NSArray* heroHealthBars;
#property (weak, nonatomic) IBOutlet UITextView* reporter;
That describes the ViewController Interface. Now, each Person class handles modification of the Person, in addition to how the Person reacts to attacks.
#interface Person : NSObject
{
__weak Profile* profile;
__weak PersonImageView* personImg;
__weak BattleModel* currentBattle;
Reaction* currentReaction;
}
// Reaction happens in the following methods:
-(BOOL) targetedFoeWithMove:(SelectedMove*)selMove log:(UITextView*)reporter;
-(BOOL) wasTargetedByMove:(SelectedMove*)selMove log:(UITextView*)reporter;
-(void) hitFoeWithMove:(SelectedMove*)selMove log:(UITextView*)reporter;
-(void) wasHitByMove:(SelectedMove*)selMove log:(UITextView*)reporter;
As you can see, I currently violate MVC because the reporter is passed into the model Person, and also a pointer to the personImg, which the VC should handle. Anyways, as the battle happens, the Reaction* is queried inside the various targeted, hit routines, and that's where modifications happen, and the reporter is notified. Notice how the Person has NO IDEA whatsoever which index its corresponding PersonView is occupying in the grand scheme UIViewController, so when currentReaction triggers in some deep routine, and passes up the data, the ViewController has no idea which UIView to update. It knew at one point (when the Person was created), but the heroImages and villainImages array gets shifted around a lot (even switching sides if one Person goes from hero to villain), so the index it had at birth isn't the index it necessarily has at the point when a Reaction triggers and needs to update the PersonImageView. That's a bit of an annoyance, so I might need to have data passed back up to the UIViewController, telling it which PersonImageView just underwent a Reaction (how would I do that?).
Here is a sample Reaction, just for completeness and helping you understand the system:
-(void) wasHitByMove:(SelectedMove*)selMove log:(UITextView*)reporter
{
[self.currentReaction personWasHitByMove:selMove inBattle:self.currentBattle log:reporter];
}
And depending on if the Reaction is relevant (there are several subclasses of Reaction that do different things), a modification like "loseLife:(int)life" is called through a Person property setter, and that's when I need the report/animation to be bubbled up to the VC. Right now, the erroneous code (bad design) is simply going directly to the personImg for the animation, then the reporter for the textual notification. I realize this is bad design, which is why I'm here, but it isn't as simple as telling the VC through a callback, "(BOOL) Hey, something happened to me." because which PersonImageView is "me"? How does the ViewController, in the middle of its own method executing the logic of the battle, know to respond immediately to the callback? How is the callback even made? (Re: Delegates. So attach the delegate protocol to Person, PersonImageView, and UIViewController? And since they all follow the protocol, then they can interpret the common message through the protocol method?)
I also looked in NSNotification but find that too complicated for this purpose; it seems like it's more of a passive Observer type of thing, where in my application, when a Reaction triggers, I need the UIViewController to immediately update the screen with UITextView text + PersonImageView animations. Immediately in the sense that before the Reaction can trigger again, the animation needs to happen, as it could involve the PersonImageView recoiling, or falling down, or changing appearance. It isn't a real-time game, but the next attack will have already been queued and will be coming momentarily after the one before has triggered the currentReaction, so any kind of delay or passive observation might be bad.
Delegates.
Suppose you have a view with a button. Give it a protocol:
#protocol MyButtonViewDelegate
#required
- (void)buttonPressed;
#end
Now your view with a button has a property like this:
#property (nonatomic, weak) NSObject<MyButtonViewDelegate> *delegate;
Which is its reference to the view controller.
The view with a button's class will have a method like this which is hooked up to the button:
- (IBAction)buttonPressed:(id)sender {
[self.delegate buttonPressed];
}
Your view control conforms to the MyButtonViewDelegate protocol and sets itself as the delegate to the view. It then implements the method buttonPressed:
- (void)buttonPressed {
// do whatever you want to do when that button was pressed
}
If you need to pass a reference to the view in this method, you can do that too:
#protocol MyViewDelegate
#required
- (void)someMethod:(id)sender;
#end
Then:
- (IBAction)someUserInteractionWithView {
[self.delegate someMethod:self];
}
The delegate then implements the method from the protocol:
- (void)someMethod:(id)sender {
// sender is a reference to the view which called this method on delegate
}
You can write a protocol method to send as many arguments as you want. You can even have these methods return values that you make use of:
#protocol MyResizableViewProtocol
#required
- (CGRect)newSizeForResizableView:(MyResizableView*)resizableView;
#end
Something within the view class triggers this method to be called on the delegate and request a new size. The delegate implements this method:
- (CGRect)newSizeForResizableView:(MyResizableView*)resizableView {
return CGRectMake(0.0f, 0.0f, 100.0f, 400.0f);
}
And the resizable view makes use of the method as such:
- (void)timeToResize {
self.frame = [self.delegate newSizeForResizableView:self];
}
As far as I'm concerned, understanding protocols and delegates is one of the more important aspects of iOS development.
Bocks are a great way to implement callbacks in objective-c. Here is a link to Apple's documentation. Apple already implements blocks all over their APIs with NSOperationQueue, NSOperation, view animation, and many others. Check out this article about communication patterns in iOS, it explains the right circumstances for using delegation, blocks, notifications, and others.
There are several ways to do this. One way is by notifications. Have your models send out notifications when these interesting events happen. Your view controller can then sign up to receive these notifications and act accordingly.
In each model, do something like this:
[[NSNotificationCenter defaultCenter] postNotificationName:#"somethingInModel3" object:self];
In the view controller, do something like this in init or viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleModel3Event:)
name:#"somethingInModel3"
object:nil];
The selector specifies the method that will handle the notification. You need to write that method as appropriate.
You'll also need to put (e.g., in dealloc in the view controller):
[[NSNotificationCenter defaultCenter] removeObserver:self];
I want to share a variable between a few ViewControllers in a tabbed application. I tried using the [NSUserDefaults] to save and load the variables but the application crashes each time.
Here is my code in the SecondViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
totalApples = [[NSUserDefaults standardUserDefaults]integerForKey:#"numberOfApples"];
[self setText:[NSString stringWithFormat:#"%g", totalApples] withExistingAttributesInLabel:self.l1];
}
It highlights the [super viewDidLoad]; when I click on the tab to open the second view as the cause of the crash.
Just in case : if you just need to share data between several VCs, NSUserDefaults may not be the best way for your Model. In that case, you may want to consider creating a class where the shared data is located, using the benefits of the singleton design pattern.
A tabbarcontroller contains a number of same-level viewControllers (as opposed to a UINavigationController, which contains hierarchical data, so the first viewController passes part of the data on to the next).
Those viewControllers need either:
- some object to hand them their data
- some object they can get the data from.
The second approach requires that those viewControllers have knowledge about the object that can give them their data and is therefore considered rigid design.
The first approach implies that you have some higher-level object that can get to the data (or contains it already) and can give it to the viewcontrollers. This is a much more elegant solution as the viewCOntrollers will be more pluggable.
The object that you could use here would be a subclass of UITabBarController. This object contains ('knows about') the viewControllers. If this object contains the dat (or can retrieve it), this object would then be able to give it to the viewControllers.
As LudoZik (#LudoZik: I wanted to upvote your answer, but was not allowed due to not having enough rep.). pointed out, create a custom class (or for simplicity an NSDictionary is also OK) that holds the data. This can then be owned by the subclass of the UITabBarController and given to the sub-viewControllers when necessary (e.g. when selected, or maybe already when loaded).
I have a app, where I've a tabview controller. All the data is dynamic, and when I enter on one tab, the data is loaded, but if I change my tab and come back to the initial tab, I haven't lost the data on it, what is awesome. My problem now is that I've built a new viewcontroller (outside the tabs) and when I go into it, and come back to the tabs I've lost all my information!
Is there any way to retain the initial data? So there when the user goes to that another view, and comes back, don't have to lose the data.
And another question. Is there anyway, to define variables that are available to every viewcontroller's in the app?
Data will not change when you move from one tab to the other
You will need to check if you have some special code in your viewWillAppear, if you load the data in this function you should know that viewWillAppear gets called when you travel tabs
About the global Data, you could define them in your appDelegate class, add properties to the appDelegate and then you can access them like this
//Add this on the header of your class
#import "MyAppDelegate.h"
//Then access the delegate like this
MyAppDelegate *myAppDelegate = [UIApplication sharedApplication].delegate;
//Access your variables
myAppDelegate.myVariables;
What is this general data? If it is objects, I would call retain. But if it was a data type, try making is static and make a method returning it. Or you could wrap it in an object, (like NSNumber for example if it was a float, double or int etc.) then call retain to that.
For example, say I have a RootViewController class and AnotherViewController class, and I need to change a property in my RootViewController from AnotherViewController... is it safe to have a "RootViewController" property in AnotherViewController.h (so that I can access its instance variables)?
#interface AnotherViewController : UIViewController {
RootViewController *rootViewController;
}
#property (nonatomic, retain) RootViewController *rootViewController;
#end
#implementation AnotherViewController
#synthesize rootViewController;
- (void)someMethod {
// set the data was added flag, so the rootViewController knows to scroll to the bottom of the tableView to show the new data
self.rootViewController.dataWasAdded = YES;
// if the user came in via a search result, make the search controller's tableView go away
self.rootViewController.searchDisplayController.active = NO;
}
If that's not a good idea, can anybody explain why?
In the code above, I know I could have used a protocol/delegate to handle the same thing - and I'm guessing I probably should. However, none of the books or other materials I've read has really discussed this.
The reason I'm asking is that I'm in the process of making my app universal, and using a UISplitViewController I've noticed that I need to often update my "master view" as the user makes changes in the "detail view". So, I took what seemed the easy route and started setting UIViewControllers as properties... but I'm experiencing some hard to track memory leaks and occasional crashes. I read something about "circular references", and wonder if that could be part of the issue (I do have a couple of places where UIViewControllers are set as properties of one another).
Thanks for any insight, or pointers to reference materials that cover this.
I'd avoid making a habit of this as there are better safer alternatives. Using a protocol/delegate is the preferred Apple way of managing data across classes. You can also set up NSNotifications to send/trigger data/events from one class to another. Key Value Observing (KVO) is also a decent way to listen in for changes.
In MVC structuring, the child views and downstream controllers really should have no idea (aka, keeping references) of their parents. It should always work the other way around where the parents manage and keep track of the children.