I have always thought that calling [super viewWillAppear:animated] is mandatory. But today I came across a piece of code from Apple's example application called CoreDataBooks and here's viewWillAppear: implementation from there:
- (void)viewWillAppear
{
[self.tableView reloadData];
}
Note there is no call to super. This confuses me a lot. If even Apple's code doesn't include call to [super viewWillAppear:] then maybe I can always omit it? Or maybe there are certain rules about this matter? Can anybody explain it?
Even though not calling the super implementation can be harmless sometimes, you should ALWAYS call [super viewWillAppear:], regardless of the bad examples Apple publishes. Not doing it may lead to very nasty issues to track down.
According to the documentation
If you override this method, you must call super at some point in your implementation.
The specific example you mentioned is broken in a more subtle way: they implemented viewWillAppear instead of viewWillAppear:, so that piece of code is not even being executed!
in short: always.
if you don't that can cause issues that are hard to track
Related
I placed my code for iAd/AdMob ads in...
-(void)viewWillAppear:(BOOL)animated{}
Ads work perfectly fine the way I have them now on all iOS devices.
When I connected my iPhone to Xcode and clicked on Product -->Analyze a message states...
The viewWillAppear:instance method in UIViewController subclass 'iPhoneSIX' is missing a [super viewWillAppear:] call
I just accidentally stumbled upon this Product-->Analyze thing. Do I really need to add [super viewWillAppear] even though everything works perfectly fine on all devices as it currently is. Will Apple reject my app if I don't pay attention to the Product-->Analyze issue navigator?
Also, what does ...
[super viewWillAppear:YES];
What does calling this do?
According to Apple: (emphasis mine)
This method is called before the receiver's view is about to be
added to a view hierarchy and before any animations are configured for
showing the view. You can override this method to perform custom tasks
associated with displaying the view. For example, you might use this
method to change the orientation or style of the status bar to
coordinate with the orientation or style of the view being presented.
If you override this method, you must call super at some point in your
implementation.
Apple doesn't gets that specific when deciding to Accept or Reject your app. It only follows the guidelines, which doesn't get that much into the weeds of your specific methods.
Calling [super viewWillAppear:YES] is a best practice, and I would recommend it. Always including super ensures that any code in the super classes get called before executing any additional code. So if you or someone else coded a super class that expected some code to be executed, you are guaranteed to still execute it, rather than just overwriting the whole method in the subclass.
Say you have a view controller of type MyViewController which is a subclass of UIViewController. Then say you have another view controller of type MyOtherViewController, which is a subclass of MyViewController. Say you're coding now some things in viewWillAppear in MyOtherViewController. If you call super first, it will call viewWillAppear in MyViewController before executing any code. If viewWillAppear in MyViewController calls super first, then it will call viewWillAppear in UIViewController before executing any code.
I'm quite certain Apple will not reject your app for failing to call super on an overridden method, primarily because there are cases where you may specifically want to avoid calling super.
That said, as Josh Gafni mentions it is definitely a best practice to do so, unless you have a very good reason for not. Also bear in mind some view controller subclasses (can't recall specifically which ones, but maybe UICollectionViewController) will only work properly if their view lifecycle methods get called appropriately, so not calling super can definitely break some classes (sometimes in subtle ways you may not realize).
Therefore my suggestion is add the call to super (generally as the first line in the method) and see if things continue to work fine. If not, spend a bit of time trying to understand what is happening differently and see if you can solve it in a different way. In general you should always (as a force of habit) provide calls to super on any view lifecycle methods you override whenever possible.
Does anyone have an official reference for why methods such as 'viewWillDisappear' should not be called directly? There are several existing posts on the subject but no official link, only opinions.
It does not make sense to do so as it is calling a method "out of cycle" from the lifetime management of a view. They can be, and in many cases are, overridden of course.
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/RespondingtoDisplay-Notifications/RespondingtoDisplay-Notifications.html
The issue in question is for some code I encountered where 'viewWillDisappear' is being called from some method. It is really the content of the 'viewWillDisappear' method that is needed to be called.
Example:
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// The following two methods are the ones that need to be called below
[self someMethod];
[self anotherMethod];
}
- (void)delegateMethod
{
[self viewWillDisappear:YES];
// Do some other work
// View is moved off-screen, not deallocated, and therefore, does not "disappear"
}
Instinctively it appears wrong to call any of the view hierarchy methods directly (e.g. viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear). If you tell 'self' and the 'super' view that viewWillDisappear it may do something in the framework that could cause problems later on. I think these methods should be called by the framework only. However, this is my opinion and not an official source. The header files don't seem to provide anything about this.
Any help appreciated.
There is no technical reason you can't.
But you shouldn't.
It's bad coding practice and you might confuse the reader or, worse, make him/her not trust you.
Good coding practice would be the following:
- (void)delegateMethod
{
[self doCommonWork];
}
- (void)viewWillDisappear:(BOOL)animated
{
[self doCommonWork];
}
- (void)doCommonWork
{
// …
}
my code snippet:
- (void)viewDidUnload{
[super viewDidUnload];
self.statusView = nil;
self.tableView = nil;
self.noDataView = nil;
}
In a rare situation, my app crashed in line self.noDataView = nil;. When I debug by po self, it seemed that it's pointing something other than current controller. What is possible reason?
PS:self.tableView's delegate and dataSource is set to self in init method. Does that have any relation to this?
First, [super viewDidUnload] should be used as the last statement. However, that won't fix your error, probably.
The reason for your problem is quite simple. Your controller is overreleased somewhere. Do you have zombie detection enabled? The code where the application crashes is usually irrelevant because the problem happened earlier.
viewWillUnload is deprecated now, you can't count on it anymore, any question about it will lead you to the below references.
From Apple:
In iOS 6, the viewWillUnload and viewDidUnload methods of
UIViewController are now deprecated. If you were using these methods
to release data, use the didReceiveMemoryWarning method instead. You
can also use this method to release references to the view
controller’s view if it is not being used. You would need to test that
the view is not in a window before doing this.
And Quote WWDC 2012:
The method viewWillUnload and viewDidUnload. We're not going to call
them anymore. I mean, there's kind of a cost-benifit equation and
analysis that we went through. In the early days, there was a real
performance need for us to ensure that on memory warnings we unloaded
views. There was all kinds of graphics and backing stores and so forth
that would also get unloaded. We now unload those independently of the
view, so it isn't that big of a deal for us for those to be unloaded,
and there were so many bugs where there would be pointers into.
Edit:
For your problem in iOS 5.1, viewDidUnload is used to release anything you have made when the view was created, so unless you are creating or retaining objects it in viewDidLoad or your nib, you may not release them in viewDidUnload.
In new iOS 6, viewDidUnload is deprecated and we have been instructed to use didReceiveMemoryWarning instead, to manage objects in UIViewController instances and subclasses. Is it equally effective to assign nils to UIView kinds inside didReceiveMemoryWarning like the way it has been done inside viewDidUnload?
I am asking this because these two methods seems to be working differently. It seems like didReceiveMemoryWarning doesn't guarantee viewDidLoad to be called again to re-instantiate any necessary UIViews.
I suspect with iOS 6, memory management is done without requiring to manually deallocate UIView. Please help me to know what I have missed in understanding the lifecycle of UIViewController.
My preferred method is now the following:
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
if (self.isViewLoaded && !self.view.window) {
self.view = nil;
}
// Do additional cleanup if necessary
}
Note that the test self.isViewLoaded is essential, as otherwise accessing the view causes it to load - even the WWDC videos tend to miss that.
If your other references to subviews are weak references, you don't have to nil them out here, otherwise you want to set them to nil, too.
You should get rid of viewDidUnload completely, and every code there should move to appropriate places. It wasn't guaranteed to be called prior to iOS 6 before anyway.
In the iOS reference for viewDidUnload:, it states that this is deprecated for iOS 6 because
Views are no longer purged under low-memory conditions and so this
method is never called
It doesn't say anything about placing this code in didReceiveMemoryWarning:. Since views are no longer purged under low memory conditions, you never have to worry about cleaning up your views in either method.
The answer by Eiko is not correct, and we should NOT set self.view to nil when receiving low memory warning. Doing so is useless and may be harmful.
iOS 6 will automatically freeing bitmaps of views which is not currently displayed, See http://thejoeconwayblog.wordpress.com/2012/10/04/view-controller-lifecycle-in-ios-6/ for details.
When providing an implementation of viewWillAppear, viewDidLoad, viewDidAppear, loadView etc.
Should the calls to the superclasses corresponding methods be made before or after performing custom action?
What are some possible consequences if performed in the wrong order?
i.e.
should it be:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
stuff
}
or
- (void)viewWillAppear:(BOOL)animated
{
stuff
[super viewWillAppear:animated];
}
etc.
For the vast majority of things you'd like to do, it won't make any difference at all. It's convenient to place the call to "super" first, because then it's easy to check later an make sure you are calling super. Apple's documentation just states that "you must call super at some point in your implementation".
There is one case where it is more likely to matter. If you are not inheriting directly from UIViewController, but instead from another custom class, then you should research the specific behavior of that class in making your decision. In general, calling super first makes for a good design pattern to make it easy to always predict behavior when debugging.