Label text not change after didReceiveMemoryWarning - ios

My app works great but when I simulate didReceiveMemoryWarning some weird things happen.
For example, in one view I have a UILabel and in the view Controller I change its text.
But, when I simulate didReceiveMemoryWarning, the text changes are not update in the view, altough from the log I see the correct value.
For example, in my navController I have at some point:
NSLog(#"day label %#",self.dayYearController.labelData.text);
and the log says
day label 8 mer 2014
But in the view of self.dayController the labelData has not that string!
Why?
Moreover, also the drawRect: method of sub views of self.dayYearController are not called anymore after firing didReceiveMemoryWarning signal.
[SOLUTION]
I realized that in navController I release the subviews if I receive a didMemoryWarning signal, but when I re-create the sub view I did not add the controller as children controllers. The process of re-creation of sub view must include also the addition of their controller

the messages will appear in the following order:
didReceiveMemoryWarning
viewDidUnload (which can be caused by 1) - obviously only applies to View Controller Classes.
dealloc
so after simulation your IBOutlet set to nil before dealloc and your view will not be able to update

Related

iOS Restoration - why is another View displayed before the one I'm restoring?

My storyboard has: NavigationController -> UIViewController 1 -> UIViewController 2 -> UIViewController 3 -> UITabBarController -> four UIViewControllers (a,b,c,d)
I'm adding restoration support to the app. I've added it to UIViewController #1 and #3 and am now adding it to UIViewController 'c' under the UITabBarController.
When I kill my app using Xcode when it's displaying UIViewController 'c' and then start the app again the viewcontroller's methods are called correctly and the content of its UITextFields are restored correctly.
But in placing breakpoints in 'c' view controller I can see that UIViewController 3 is temporarily displayed. It's viewDidLoad and viewWillAppear: methods are called.
What would typically be the cause of UIViewController #3 being displayed?
I've pressed on implementing restoration for my UIViewControllers and there doesn't appear to be any negative effect from an end-users point of view - without the breakpoints the display doesn't get a chance to update to show the intermediate view.
That's what view controller restoration is. The app launches from scratch; to reach the view controller that was being displayed at termination, the app walks the view controller creation sequence down the chain from the root view controller to the view controller that is to be displayed to the user. It couldn't possibly happen any other way. (After all, if VC2's Back button is to lead to VC1, then VC2 must be above VC1 in the nav controller stack.) And you wouldn't want it not to happen, because you might have important work to do as the sequence is reconstructed. By setting breakpoints in the middle of the restoration process, you have revealed "the man behind the curtain". But the user doesn't see that process (it is covered by the launch image), so there's no problem.
In particular it is very important that viewDidLoad is called in correct sequence right down the chain of creation, and that this happens for all view controllers before they are sent decodeRestorableStateWithCoder:, also in order down the chain.
(However, you should not count on anything having to do with viewWillAppear: and its cousins. These may or may not be called and you can't be sure just when. I regard this inconsistency / unpredictability with regard to the timing of viewWillAppear: as a bug, but Apple, unfortunately, does not.)

Crash on orientation change after calling setViewControllers:animated:

I've been dealing with this (somewhat) random bug for a while and can't figure out the problem. The context: I'm creating an UISplitView iPad app that have a UINavigationController inside the Master view:
Main menu in red, submenu in green and main content in purple.
This UINavigationController does not fill the entire Master view because I need some space to have a vertical menu. When a user select a button on the vertical side menu, it sets something new to the UINavigationController to show a UITableView with options. What I'm doing on every menu selection is:
[self.subMenu setViewControllers:#[subMenuViewController] animated:YES];
What happens is that I don't need to keep the menu history, so what I do is I set a new root view controller to the subMenu every time.
The issue is when I start messing with the device orientation. It doesn't have a clear pattern, but sometimes, when rotating, my app crashes. Now when I run it with Instruments, this is what I get:
167 Zombie -1 00:32.101.527 UIKit -[UITableView _spacingForExtraSeparators]
And the interesting thing is that the bad access happens on the previous root view controller of the subMenu. So if I tap "Events" and then after that I tap "Podcasts", the bad access happens on trying to access "EventsViewController".
So I'm guessing something is not right on my way of replacing the root view controller of the subMenu UINavigationController, but I'm not sure what it is. Maybe I need to make sure the current root view controller is released before setting a new one?
Any help is much appreciated. :)
It's not uncommon to crash in system library code due to something you didn't set up quite right. This might be that your UIWindow, UIApplicationMain or its content view or your view controller instance was not retained or got released somehow.
That shouldrotate method won't help if your controller isn't still around.
This is to identify which object got released.
For particularly thorny problems, you could add release, retain, and dealloc methods (that log and call super) to a suspect class of yours and see what's releasing it. Log the -retaincount to keep track (I only use this for diagnostic purposes,)
Or you can try this, set a breakpoint on -[UIDevice setOrientation:] and step through your code in the debugger.
To make debugging easier, you can type call (void)instrumentObjcMessageSends(YES) right in the debugger console to begin logging objc_msgSends to /tmp/, then continue execution and it will trace all the messages that are sent right up until the crash.
So first, you should implement the methods willRotateToInterfaceOrientation, willAnimateToInterfaceOrientation and didRotateToInterfaceOrientation (check the actual signature of these methods) in your view controllers that contain a UITableView.
In each of these methods check the your table view dataSource and delegate. I think this crash is caused by a release of the delegate or table view's datasource.
Also you should check what table view delgate/datasource methods are called during rotation.
And the last thing, make sure you discard the old instances of subMenuViewController and they are properly removed from the parent view controller.

About viewController's "viewDidLoad" and "viewWillAppear" methods

I've got a question regarding the two mentioned methods, since in my tests I don´t make clear the order they are called. I thought that, firstly, viewDidLoad is called when the viewController is loaded for first time (as the name indicates), and inmediately after the init method. Then, I thought that once viewDidLoad returns, viewWillAppear is called. If you display another viewController, and then you return to this one, then it should be already loaded and only viewWillAppear will be called.
However, while developing I make the impression that there is no order when calling viewDidLoad and viewWillAppear... I couldn´t find a clear description of this lifecycle in Apple's documentation, how does this actually work?
Thanks!
I would like to add to Caleb's answer: Don't confuse the view controller and the view! The name viewDidLoad clearly indicates that the method is invoked after the view has been loaded. It is the view controller that does the loading.
Some pointers regarding the lifecycle of views and the order in which messages are sent:
Not an official Apple document, but I find this diagram really useful because it includes pretty much all of UIViewController's lifecycle overrides.
In the section Resource Management in View Controllers from Apple's "View Controller Programming Guide" there is a flowchart that depicts how views are initially loaded. It explains loadView and viewDidLoad, also in conjunction with storyboards.
The section Responding to Display-Related Notifications from Apple's "View Controller Programming Guide" explains how to respond to views appearing and disappearing (viewWillAppear: et al)
If you are planning on implementing a container view controller: The UIViewController class reference has a good overview of how messages need to be sent by your subclass.
I'm stopping here. You can find more stuff yourself by googling for "uiviewcontroller life cycle".
-viewDidLoad is called when the controller loads its view, which is not necessarily right after initialization. View controllers don't load their views until they need them, either to display or for any other reason.
-viewWillAppear is called just before the view is displayed. This will be after -viewDidLoad, but you don't know exactly how long after. -viewWillAppear is called every time the view is displayed; -viewDidLoad will only be called a second time if the view is unloaded at some point (such as didReceiveMemoryWarning). These days that's unusual, but it can happen.
Or if the viewController is set to nil, which can usually happen if a view controller is kicked off the navigation stack, and therefore next time it is brought to the navigation stack it needs to call -viewDidLoad again.
I thought that, firstly, viewDidLoad is called when the viewController
is loaded for first time (as the name indicates), and inmediately after the init method
No. The name indicates that the controller's view has been loaded (not the controller itself). Actually the docs state that this method will get called after the view hierarchy has been loaded into memory (either via loadView or through a nib for example).
Then, I thought that once viewDidLoad returns, viewWillAppear is
called
Again, no. loadView (and as a consequence viewDidLoad) method will get called the first time that view property is to be accessed and is nil (which is the case when you're initializing a controller). Think of this simple scenario:
MyViewController *vc = [[MyViewController alloc] init];
UIView *view = vc.view; // <= loadView & viewDidLoad will fire but it certainly didn't appear...
However, while developing I make the impression that there is no order
when calling viewDidLoad and viewWillAppear...
Well there is an order. We know for sure that viewWillAppear will always be called after viewDidLoad (if both of them are to be called of course).
As you said, ViewDidLoad is only calling once after loading the view. So we can initialize the instances in the viewDidLoad. It is mainly meant for the initialization.
viewWillAppear will invoke whenever we reach to this view. So if there is any changes in UI, we can done it in viewWillAppear.
I ran a trace on when all these calls are made: http://thecodist.com/article/ios_arc_storyboards_and_uiviewcontroller_trace

UIViewController - View events life cycle and registering for KVO / Notifications

I'm wondering if there's any way -viewWillAppear: would be called without a matching -viewDidAppear:. Ditto for -viewWillDisappear and -viewDidDisappear.
The root of my question is where to register and unregister for KVO and / or NSNotifications of an object who's change notifications will cause the view controller to update views.
For example, I have a model object that is being processed asynchronously and it's string properties could change. I'd like the view controller to KVO those properties and have any changes reflected by swapping out the text of a label managed by said view controller.
Where do you register and unregister for notifications and why?
EDIT:
A gotcha I've come across is what to do in cases of application state change (e.g. -applicationWillResignActive, -...didEnterBackground, etc). These changes don't seem to trigger view controller lifecycle methods. Any best practices here?
With the standard container view controllers, you will always get will/did messages in pairs. If you have written your own view controller container, you may not, but that would be a bug in the container implementation.
Most of the time, you'll want to set things up and tear things down in the 'will' messages. This gives your view controller the earliest shot at anything it needs to do before it becomes "active" and also shuts down things as early as possible when you no longer need them.
When pushing a view controller in nav stack, it is entirely possible that a notification will occur during the push animation. If you set up the observers in viewDidAppear, you would miss that notification. You want to be listening as soon as possible.
Likewise, I would reckon that viewDidDisappear is too late to remove callbacks. For example, a location manager could be stopped in viewDidDisappear, but another location update could be delivered during the disappearing animation. That probably doesn't hurt much, but depending on the application, something weird could happen like an alert view appearing after you've already left a view controller - which looks like a flicker to the user.
Anything non-view related, as indicated above, occur in the 'will' methods. The choice about will vs did, then, is really about what the user sees. Animations should start in viewDidAppear, otherwise, the user won't see the frames that occur during will/did appear. Data should be moved to views in viewWillAppear, otherwise, a blank view will transition in and the data will only appear after the transition animation completes. Also, it possible that a view frames could be adjusted in between viewWillAppear/viewDidAppear, like in the case of a previous view controller in the stack hiding/showing the navigation bar.
And on a side note, not something I will get into great detail here with, but I'd advocate against KVO for controller interactions that move data from model to view objects. Difficult to test and difficult to trace.
You can subclass your UILabel and in your subclass override the setText method:
-(void)setText:(NSString *)newText {
//do your KVO updates here
[super setText:newText];
}
hope this helps

Custom UIViewController viewDidUnload never gets called

I have a custom UIViewController subclass that handles all the view initialization by itself (it doesn't use nib). There is also another UIViewController subclass loaded from nib. Both are contained by UITabBarController.
When a memory warning comes, the first controller does receive notification, but viewDidUnload doesn't get called. The second controller also receives notification and it's viewDidUnload does get called.
I checked in didReceiveMemoryWarning, self.isViewLoaded is TRUE and self.view.superview is null.
Both controllers (their tabs) are invisible at the time the notification appears.
Is there something special a custom view controller should do to be unloaded in a result of memory warnings?
If you are subclassing UIViewContoller and you do not initialize it from a NIB, you need to subclass the -loadView method. Otherwise iOS assumes that the view cannot be unloaded / reloaded.
It would be sufficient to just add the following to your implementation:
- (void)loadView {
[super loadView];
}
I will try to find a documentation quote for that.
The documentation is unfortunately not very clear on this matter.
I would check out the documentation on the View Controller Lifecycle. Specifically, check out the section on what happens when the memory warning is received. If your custom view controller's view is the view on screen, the OS will not attempt to release this view. Is this view on screen when you're getting the memory warning? In the simulator, navigate to the nib-loaded view and simulate a memory warning, see if your custom view gets released then. Also, see if viewWillUnload is being called. And make sure that in any of these methods that you're overriding that you call super.

Resources