When should UIViewController add/remove observers on NSNotificationCenter? - iphone

If I add an observer to the [NSNotificationCenter defaultCenter] in my viewDidLoad should I be removing it in viewDidUnload?

If you need to add these in your initializer, you should remove it in the dealloc method. Ideally, you should only care about these notifications when you are currently onscreen or not.
The viewDid[Appear|Disappear] methods can be called multiple times during the lifetime of a UIViewController. Register for the notification in the viewDidAppear method and unregister it in viewDidDisappear.

You should remove it in dealloc method.

It seems to me viewDidUnload is the place to put it.
If the notification handler that gets called accesses any of the views managed by the view controller, that will either be an error or will cause the view to get reloaded unnecessarily. If your view is not being shown, then most likely the view controller doesn't need to be notified. If it does, at least check if the view is loaded before you make any changes to it. While the view is not loaded, you might still need to update the state of your view controller, for example change or dirty cached values, but don't update the view until it loads again.
Two, what happens if you don't removeObserver in viewDidUnload, and viewDidLoad gets called again? You call addObserver again. Probably doesn't hurt, the notification center can detect duplicate adds.

Related

In iPhone, is it guaranteed that viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear are always called?

I'm using viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear of UIViewController.
And I use UINavigationController to navigate view controllers.
For example, I create NSTimer or register notifications in viewWillAppear or viewDidAppear. And I invalidate the timer or remove notifications in viewWillDisappear or viewDidDisappear.
But if those 4 methods (viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear) are not called, the program will crash or retain cycle will happen.
I couldn't find the documentation saying that it is guaranteed that viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear are always called.
Till now, they seem to be always called and my program works as I expected.
But are there cases that the 4 methods are not called?
Or is it possible the 4 methods are usually called but sometimes not called randomly with no reason?
I can say than viewWillAppear and viewDidAppear methods are always called in your view.
viewWillDisappear and viewDidDisappear methods are called when you change your view with another one or close it. If you have some scrolling views for example, these methods won't be called.
Yes, they are always called. Just make sure to call the superclass implementation if you subclass one of your view controllers.
I think you can trust those methods, still, viewDidLoad and dealloc for me never failed.
You can register and remove notifications there. But, I don't know all of your app specifications and what I recommend might not be viable.
Yes the View events always fire, however to override the behaviour you must declare them.
According to apple documentation viewDidUnload is deprecated.
Look at this link for the official apple class reference for UIViewController
UIViewController Reference
It states:
The UIViewController class provides specific methods that are called when these events occur. Subclasses can override these methods to implement specific behaviors.
Here are the same thread in which same thing asked m any times
you can view these here
here the first link
here the second one
Aside from bugs when some of the methods may not get called and/or called twice (e.g. dismissing/popping several controllers at once), with iOS 7, if you initiate the swipe-to-back gesture, partially revealing the previous controller in the navigation stack, and then cancel the swipe-to-back, so that no popping occurs, you will observe that not all of the methods get called on both of the controllers.

How does iOS decide which objects get sent didReceiveMemoryWarning message?

I am working on an iPhone application in which a number of UIViews are dynamically added to and removed from the main UIWindow.
When simulating low memory errors in the simulator, I have discovered that not all view controllers receive the didReceiveMemoryWarning notification. Unfortunately, these are the controllers that would most benefit from implementing this method.
I cannot seem to find good information about where and how the method gets called. I have read mentions that it gets sent to "all UIViewControllers", but this is evidently not the case. Adding a breakpoint in one of the classes that do receive the notification wasn't particularly enlightening either.
This is a complex project but one way these views get added is:
- (void) showMyView
{
if(!myViewController){
myViewController = [[MyViewController alloc]init];
[window addSubview:myViewController.view];
}
}
MyViewController is a subclass of another class, MySuperViewController, which is itself a subclass of UIViewController. None of those classes have corresponding NIBs; view hierarchies are created programatically.
I am looking for pointers to how I can go about diagnosing the problem.
When you are using .view of the view controller directly, there's a high chance that your view controller won't receive many notifications because it's not the correct way of using view controller. The UIWindow is special case, because the window can automagically know the controller of the view and direct the message to the controller correctly.
However, when you detach your view from UIWindow, the view controller is also detached and not managed by UIWindow any more. I think this is the source of the problem.
I would suggest that you add a navigation controller or tab bar controller as your root view controller, and use that view controller functionality to switch between your child controllers. Note that you should not remove your view controllers when switching so they will be able to receive the messages appropriately.
You might also considering releasing your view controller when not used if initialization of your view controller is trivial and not consuming too much time.
Somewhere in your code you are probably doing something like this:
[[NSNotificationCenter defaultCenter] removeObserver:self];
The only safe place to do this is in -dealloc.
Everywhere else, you should specify the notification that you want to unregister for (this will still potentially break if you register for the same notification as a superlcass).
From the documentation
The default implementation of
[didReceiveMemoryWarning] checks to
see if the view controller can safely
release its view. This is possible if
the view itself does not have a
superview and can be reloaded either
from a nib file or using a custom
loadView method.
This method gets called when a Memory Warning "happens"/is simulated. When the memory is low, the system probably posts a notification and a view controller responds to the notification by calling didReceiveMemoryWarning.
If you do not override the method, the default implementation (described above) is called. All view controllers in memory receive the Memory Warning and call this method. They just don't do anything if it is not "safe" to release the view.
In a simple test application with a navigation controller, in both the current view controller and the one previously displayed, didReceiveMemoryWarning is called. I don't know how the NSNotificationCenter works exactly, but it knows who registered for the UIApplicationDidReceiveMemoryWarningNotification. It is probably set up something like this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didReceiveMemoryWarning)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
For more information, you can look at the Memory Management section in the UIViewController Class Reference.
I entered this question, searching for the right observer dealing with memory warnings. For those using swift, you can register as followed:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "didReceiveMemoryWarning:", name:UIApplicationDidReceiveMemoryWarningNotification, object: nil)
With callback method:
func didReceiveMemoryWarning(notification: NSNotification){
//Action take on Notification
}
Also, make sure your custom class inherits from NSObject, or you'll be getting this error:
… does not implement methodSignatureForSelector: — trouble ahead

Calling viewDidLoad while poping from a navigation class?

Interview question: For example, I have 3 classes A,B & C. I navigate from A -> B -> C, while pushing viewDidLoad function calls automatically and during popping viewWillAppear get called. But would it be possible to call my viewDidLoad function while popping?
No, Its not possible.Only viewDidAppear and viewWillAppear will be called by itself.You can call it manually.
All the best.
The viewDidLoad method is called when the view just loaded. Then viewWillAppear is called by the navigation controller when it's about to display the view.
If you want some code to be executed when the view is about to be displayed, be it when it's being pushed or when the top one is being popped, it makes more sense to use the appropriate method viewWillAppear for that, instead of trying to call the viewDidLoad method at a time when it isn't appropriate.
Can't you just move whatever code you have in viewDidLoad to viewWillAppear?
If the question is just "how can we call viewDidLoad while popping?", then it's simple:
- (void) viewWillDisappear:(BOOL)animated
{
[self viewDidLoad];
}
Just as a side note, if you have a view controller stack like A->B, it's possible that viewDidLoad will be called automatically on A when popping B if, while B was on top, the navigation controller unloaded A's view (if the app received a memory warning, for example)
You should not call viewDidLoad manually, it's not designed to be handled this way. Use viewWillAppear, as other users already mentioned.
As for the question whether it may happen that viewDidLoad will be called upon popping from a higher view controller: I imagine that may happen when the device got a low memory warning and unloaded the view controllers further down in the navigation hierarchy. Then the OS has to reload the view, I haven't however tested this and it's possible that this will never happen. The OS only unloads views when they don't have a superview, I didn't check whether upon pushing, views down the hierarchy actually get removed from the hierarchy.
ViewDidLoad should be for view creation.
ViewWillAppear - for Data Interaction if Server Request is Asynchronous. like calling API or any functionality.or any functionality we like to call when view going to appear.
ViewDidAppear - for Data Interaction if Server Request is synchronous.

ViewWillDisappear versus dealloc

I put an NSlog in my dealloc method of my view controller. It does not get consistently called. I do notice that ViewWillDisappear does get called always. Would it be ok to move all my tidy upcode here? Setting stuff to Nil and release calls.
Anybody got some advice on why dealloc is not getting called? I know it says in docs it may not get called, but if you have a very simple App it gets called always. So something i do must be affecting the dealloc.
This is the code that calls my ViewController than isnt always calling my dealloc.
-(IBAction) playComputerTapped:(id)sender
{
PlayGameViewController *pgvc = [[PlayGameViewController alloc]
initWithNibName:#"PlayGameViewController" bundle:[NSBundle mainBundle]];
pgvc.gameMode = 1;
[self presentModalViewController:pgvc animated:YES];
[pgvc release];
}
The above code takes me from the mailmenu ViewController into the game.
Below is the code to leave the gameViewController and take me back to the menu.
[self.parentViewController dismissModalViewControllerAnimated:YES];
Thanks
-Code
Don't you mean viewDidUnload instead of viewWillDisappear ?
viewWillDisappear is called when the view controller is disappearing. This usually happens when the view controller is being popped out, or other view controller is pushed to the stack. Purpose of viewWillDisappear is to stop active actions - for example stop animations, hide some elements or similar.
viewDidUnload is probably what you meant as this one is called when view controller's view is unloaded. This should never happen for a view controller that is currently visible, only for controllers that are somewhere in the navigation stack (part of UITabBarController or UINavigationController), but not currently visible. Purpose of viewDidUnload is to release any UI elements that are part of the view and that the view controller retained as well.
To understand it's important to realize the reasons why a view for such controller would want to be unloaded. Reason is memory consumption. Views consume considerable amount of memory, even when they are not currently visible. Views are usually very easy to reconstruct - simply by calling the code that constructed them in the first place. That's especially very easy if you are using Interface Builder. So these views are best candidates to be freed to gain more memory.
When system doesn't have enough memory it starts calling didReceiveMemoryWarning method of view controllers which are not currently visible. If you have created your controllers from template in Xcode, this method simply calls super's (UIViewControllers's) implementation [super didReceiveMemoryWarning]. That default implementation will release self.view, which in turn should deallocate it together with all its subviews.
Now let's say that your view-controller needs access to some of the subviews to manipulate it in some way. For example you can have a UILabel element there and you want to change its content accordingly. To access this element you create a member variable (IBOutlet) and connect it to the element. Now your view-controller owns that label, so its retain-count is increased. When controller's view is released, so is the label, but because your view-controller still retains the label, it will not be deallocated. Therefore you should release the label in viewDidUnload method.
I've seen applications that were creating views programmatically (in loadView method), but loading was done in such dirty way that it was not possible to reconstruct the view after it was once deallocated. Therefore each time system was out of memory, it had called didReceiveMemoryWarning which in turn deallocated the view, but after navigating back to that view-controller application had crashed. A fast "bugfix" was to remove calling [super didReceiveMemoryWarning] in view-controllers. Well, system didn't get the memory and some strange effects occurred, but at least the application didn't crash immediately.
Now the third one - dealloc. This is called when object is not owned by anyone and its memory is going to be freed. Here you need to release all objects that you have retained. For view-controllers those are usually references to model classes.
I want to describe one more possible scenario. Let's say you have a view-controller displaying a chat with another person. Let's say it's very fancy chat, with emoticons and buddy-icons being animated. Let's say that each chat-entry is displayed as a cell of UITableView.
When your buddy sends you a message, you want to append a new cell into table-view by reloading it. Therefore your view-controller has an outlet to the table-view.
In viewWillDisappear you should stop the animations of emoticons and icons.
In viewDidUnload you should release the table-view.
In dealloc you want to release chat's history (probably NSArray of all messages sent and received during this conversation).
Now if you navigate away from your chat, viewWillDisappear gets called and you stop the animations.
When system is low on memory, and your view-controller is not visible, didReceiveMemoryWarning gets called and the view is released. Your viewDidUnload gets called and you release UITableView so that it can be really deallocated.
When you navigate back to the chat, loadView gets called again and your view is constructed again, viewDidLoad is called after that. Your model (representation of chat conversation) is still there, so the table-view's datasource has all the data as before, so table-view will display exactly the same thing as before the view was deallocated. After all viewWill/DidAppear is called where you start the animations.
When you finish chatting with your friend, you release the view controller, and it gets deallocated - dealloc is called, you release an array containing the chat messages, and cleanup everything else.
I hope it make things a little clearer.
Well that depends.
If you need to reduce the memory footprint of your app, unloading stuff while the view is not visible via viewWillDisappear is a good way to go. However you'll need to re-initalize everything once the view will be shown again, depending on its content this may produce considerable overhead - maybe even without the need.
For a small app (in terms of memory usage), using dealloc to unload your stuff is fine. It will be called only if the objects retain count drops to zero and is the last method that will be run before the object is destroyed. When using autorelease, that may not be the case right away and of course the object could be retained by some object other than the parentviewcontroller, preventing it from being destroyed and thus dealloc from being called.
You might want to check out the Memory Management Programming Guide, it'll explain things more detailed.

What's a good place to unregister an observer from the notification center?

When I add an observer to the default notification center, where would I unregister that?
Example: I have a UIView subclass which lives inside a view controller. That subclass is an observer for the FooBarNotification. If this notification is posted, that view will get it. But now, the view controller decides to throw away the view. Is the best place the -dealloc method of the view itself?
Are there any rules like memory management rules? For example: Must I unregister an observer where I registered it? i.e. the view registers itself in it's init method, so it should unregister itself in it's -dealloc method?
(not talking about push notifications, but NSNotificationCenter)
The only rule is to make sure the observer lives longer than the registration period.
Since -addObserver:… will not -retain the observer, if the registration outlives the observer itself, your program will crash.
Apple doesn't specify any rules to unregister the observer. -dealloc is fine. Just use common sense. E.g. if that observer may persist even after the view controller throw it away, then you should unregister at that throw-away procedure, otherwise the observer may receive unwanted notifications.
Certainly it should be done in the dealloc, but basically when you no longer need notifications.
One possible place is to override the willMoveToWindow: message on the UIView and remove the notification when the view is removed from a window:
- (void)willMoveToWindow:(UIWindow *)newWindow
{
if (window == nil) {
// view removed from a window -- remove notifications here
}
[super willMoveToWindow:newWindow];
}
I usually add my observers in viewWillAppear and remove them again in viewWillDisappear.
This works because almost always I won't want a notification to be picked up by a view that isn't currently in the foreground (any longer living observers I register in my coordinator)
Dealloc and viewDidUnload if you have registered for notifications in viewDidLoad.