Is the viewDidUnload & didReceiveMemoryWarning optional if no extra logic is added to these 2 functions (i.e. save to remove the following codes)?
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
[super viewDidUnload];
}
Yes, if no extra logic is added to these 2 functions.
But you need to inspect carefully the whether any logic is necessary?
You also need to understand when these function is run in this document by Apple
The View Controller Life Cycle
Then you decide whether you need any extra logic.
Yes.
According to the documentation, the default implementation of didReceiveMemoryWarning "attempts to release the view controller’s view". So if you don't need anything else to happen, then you can delete the code and rely on the default.
Related
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
{
// …
}
viewDidUnload is no longer called in iOS6, so as a workaround for an app that does some necessary things in viewDidUnload I have done this:
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// only want to do this on iOS 6
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 6.0) {
// Don't want to rehydrate the view if it's already unloaded
BOOL isLoaded = [self isViewLoaded];
// We check the window property to make sure that the view is not visible
if (isLoaded && self.view.window == nil) {
// Give a chance to implementors to get model data from their views
[self performSelectorOnMainThread:#selector(viewWillUnload)
withObject:nil
waitUntilDone:YES];
// Detach it from its parent (in cases of view controller containment)
[self.view removeFromSuperview];
self.view = nil; // Clear out the view. Goodbye!
// The view is now unloaded...now call viewDidUnload
[self performSelectorOnMainThread:#selector(viewDidUnload)
withObject:nil
waitUntilDone:YES];
}
}
}
Is there any precedent for Apple rejecting something like this? Due to time constraints I can't risk them rejecting anything.
There's no reason why Apple would reject it, but they removed it for a reason.
Calling it manually is just asking for problems. I strongly recommend to get the application logic right and do it the "correct" way.
Edit: After looking over that code again, I have serious doubts about your implementation. The whole construct with calling selectors (we should already be on the main thread!) and removing from superview (which just steals the view from it's owner without telling it) just cannot be correct. This is the type of code that you really want to eliminate from your code base.
There's no reason they would reject this. By deprecating the method, -[UIView viewDidUnload] simply becomes like any other method. You can treat it as if it never existed on UIView in the first place and you just happened to create a method called -viewDidUnload.
It would be a problem if -viewDidUnload still existed internally (which it might) and you attempted to call Apple's (now private) implementation instead of your own but I highly doubt Apple would do this. Just remember in your -viewDidUnload to remember to ask if the super class implements the method before attempting to call it on super if that's what you're currently doing, using:
if ([[self superclass] instancesRespondToSelector:#selector(viewDidUnload)]) {
[super viewDidUnload];
}
If you really wanted to be safe you could always move your code to a different method. Inside viewDidUnload just call your new method for iOS 5 devices and in -didReceiveMemoryWarning call your new method if you're on iOS 6.
I'm not going to comment in length on the logic in your didReceiveMemoryWarning since that wasn't in the question but I will say that you should be very careful about the state you're putting the controller in (make sure those if statements cover all of your bases!) And of course, you cannot expect your view controller and its views to be in the same state when viewDidUnload is called by you as when it was called by UIKit in iOS 5.
Prior to iOS 6, we were supposed to
- (void)viewDidUnload {
self.someDelegate = nil;
[super viewDidUnload];
}
Now that viewDidUnload is deprecated, where do we set our delegates to nil? Thanks!
Views are no longer unloaded when receiving a memory warning in iOS6. So the view is never unloaded on such cases and viewDidUnload is never called in iOS6.
If you really want to mimic the old behavior (unloading the view upon receiving a memory warning) you now have to implement this behavior in the didReceiveMemoryWarning method of your view controller, after testing that the self.view.window property is nil (meaning the view is not on screen anymore, thus will be "unloaded", meaning that you are in the same situation as the old viewDidUnload case).
-(void)didReceiveMemoryWarning
{
if (self.isViewLoaded && !self.view.window)
{
// If view already loaded but not displayed on screen at this time (not attached to any window) then unload it
self.view = nil;
// Then do here what you used to do in viewDidUnload
self.someDelegate = nil;
...
}
[super didReceiveMemoryWarning];
}
But note that if you use ARC and iOS5+, you generally won't need to set your delegate to nil anymore, thanks to the weak property attribute and the Zeroing-Weak-References mechanism that automatically reset weak variables and properties to nil if the object they are pointing to does not exist anymore (thus avoiding dangling pointers).
[EDIT] And as explained by #Martin R in the comments, views are not unloaded anymore when receiving a memory warning in iOS6, so you won't have to manage this case of receiving a memory warning and think about releasing your delegate there as this use case won't occur anymore in iOS6.
Well, I did not come up with this myself, but if should help you: "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."
http://www.bgr.com/2012/06/11/ios-6-beta-download-link-iphone-ipad-ipod-touch-release/
Prior to iOS 6, we were supposed to
(void)viewDidUnload {
self.someDelegate = nil;
[super viewDidUnload];
}
Where did you hear this?
Do you even know what viewDidUnload is for prior to iOS 6?
viewDidUnload is only called in low memory situations, which cause the view to be unloaded. It will never be used during normal operation. If you were depending for it to be called to do other things, that is wrong.
Plus, why would you ever need to set self's delegate to nil anyway? "Niling the delegate" refers to setting other objects' delegates (which point to self) to nil when self is deallocated. Setting self's delegate makes no sense (why would you care setting stuff on self if self is no longer used?).
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.
In the template Apple gives you, I see that they have:
/*
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
*/
I understand that you want your super class to do any loading of the view before your own view. For viewDidDisappear, do you do the same thing for the same reason?
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
}
Yes always call the super class method first to have it complete so then you can override any behaviour subsequently in your inherited classes method.
The only exception to this is the dealloc method where you should call [super dealloc] last to make sure you've cleared up your own class properly before any references might get lost and cause a memory leak.
I would say that things should always be taken apart in the opposite order they are built up. You call [super viewWillAppear:animated] at the beginning of viewWillAppear so the super class has a chance to set up the default actions, then you get your opportunity to modify.
For the call to [super viewDidDisappear:animated] you should do your disappearing code before calling the super one at the end of your viewDidDisappear. That way if you have any references to the default view contents they are not released before you are done with them. If you don't use anything in the super class then it is probably irrelevant.