iOS memory management with ARC - ios

Let's say i'm working on an app with a large number of views and i have some problems understanding memory management when the UIViewController segues to another UIViewController.
Which of the following object should i release in viewDidDisappear: ?
#property (weak, nonatomic) IBOutlet UIImageView *background;
#property (strong,nonatomic) UILabel *playerLevel;
- (void)viewDidLoad
{
[super viewDidLoad];
map = [[MapView alloc]init];
[self.view addSubview:map];
}
Is this the correct way to do this ?
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:YES];
[_background removeFromSupeview];
[self.playerLevel removeFromSupeview];
[map removeFromSupeview];
_background = nil;
self.playerLevel = nil;
map = nil;
}

You don't need to do anything. ARC will implement the dealloc method for you, which will call all releases for your retained properties.
I really recommend you read the memory management documentation from apple, it will help to understand what ARC is really doing, including understanding how retain cycles can be avoided.
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html

You don't need to release anything. ARC will take care of it when deallocating your view controller.

viewDidDisappear: only notifies the view controller that its view was removed from a view hierarchy. This is an optional method that your view can utilize to execute custom code when the view does indeed disappear. All views will be released automatically by ARC.

When you segue (presenting/pushing to a new scene), the previous view controller's views are generally not released. Only when a view controller is dismissed/popped (or you call an unwind segue) would it generally get deallocated. If you're seeing memory consumption continue to grow, make sure you are presenting/pushing to go forward, but dismiss/pop/unwind to return back to a previously presented view controller.
(A very long time ago, in low memory situations iOS would unload/release views that were not current visible, but Apple deprecated that in iOS 6.0 because it just didn't save much memory and caused too many problems for developers.)
Bottom line, because you're using ARC, you don't need this viewDidDisappear method at all. When the view is deallocated, that eliminates any strong references it maintains on its subviews, resulting in them being deallocated automatically, too (assuming you didn't create other strong references elsewhere, which you shouldn't be doing, anyway). Likewise, when the view controller is deallocated, any strong references it has are also resolved, resulting in those properties being released, too.
As an aside, a view controller maintains strong reference to the top level view, but it doesn't need to maintain strong references to that view's subviews. When addSubview is called, the top level view maintains its own strong references to its subviews. Thus the view controller owns the view, but the view owns its subviews.
This code sample suggest a bit of a logical inconsistency, where your IBOutlet is weak (as it should), but your label (and presumably the map) are strong. This isn't going to cause a problem, but suggests a logical inconsistency in the object ownership diagram.
I might suggest making the playerLevel property (and map, presumably) weak references (just like the IBOutlet). And, if instantiating these programmatically, you'd do something like:
#property (weak, nonatomic) UILabel *playerLevel;
- (void)viewDidLoad {
[super viewDidLoad];
UILabel *playerLevel = ...
[self.view addSubview:playerLevel];
self.playerLevel = playerLevel;
}
So, we create a local UILabel variable, configure it, add it to the subview, and then set the weak property to reference that label. The use of that local variable is important when dealing with weak properties, so it doesn't get released before you have a chance to call addSubview.

Related

Knowing when a view is going to be destroyed in UIView lifecycle

I have a UIView which is part of a UIPageViewController.
I pass a strong object to this view, and am having problem with releasing.
In the past I think I used viewDidUnload to release any strong objects, and then Dealloc is called. Now deal is never called because of the strong objects.
What is the best way to know icm with a UIPageViewController that the object is not needed anymore. I.e. if it is the view beside the page the user is looking at, it might come back into view. So using viewWillDisappeart will not work I expect.
#interface DOTourFloorPlanViewController : UIViewController <UIScrollViewDelegate, DOAsyncImageViewDelegate> {
IBOutlet DOAsyncImageView* _imageView;
IBOutlet UIScrollView* _scrollView;
NSMutableArray* _beaconLabels;
UIView* _circle;
UIView* _centerDot;
DOTour* _tour;
CGRect _zoomRect;
int _circleCenterX;
int _circleCenterY;
bool _didZoomToLocation;
}
#property (strong, nonatomic) DOTour* tour;
Views aren't unloaded any more, newer devices don't have quite such tight memory restrictions and there are other optimisations. It seems that when you say view you're actually referring to the view controller, which is a little confusing.
If your view controller (VC) is provided with some data it can retain a reference to it until it's destroyed. The data should not be retaining the VC, or its view. If you have any kind of observation / delegation then that link should be weak to prevent retain cycles.
In this way the data will be released when the VC is no longer required, i.e. when it is removed from its parent or presenter.
Specifically for Core Data and references to NSManagedObjects you can help the system by calling refreshObject:mergeChanges: with a flag of NO to turn the object into a fault and remove its data from memory. You can do this when the view did disappear.

To call addChildViewController or not to call addChildViewController

I'm fairly new to Objective-C and iOS, and I've taken on an application written by someone else. I noticed something that threw me, and I think my question varies slightly from the other questions people have asked about addChildViewController.
Well, the app works just fine. Rotations work fine. Everythings good.
So what's the problem?
Well there's a full screen view that holds a bunch of subviews in a side-by-side tiled type of layout. In other words, all of them have the same full screen parent view, and each are siblings to each other.
Those subviews are each an instance of a UIViewController.
I had expected to see those subviews set up like this...
DoohickeyController *doohickey = [[DoohickeyController alloc] initWithNibName:#"Doohickey" bundle:nil];
[self addChildViewController:doohickey];
[self.view addSubview:doohickey.view];
but instead they simply do this....
DoohickeyController *doohickey = [[DoohickeyController alloc] initWithNibName:#"Doohickey" bundle:nil];
[self.view addSubview:doohickey.view];
Is there a hard and fast rule about whether or not to call addChildViewController when the app appears to work just fine?
Is there any advantage/disadvantage to adding the call to addChildViewController?
One of the major design assumptions in UIKit is that the ViewController hierarchy will generally be in sync with the View hierarchy. Callbacks handling autorotation and size class transitions are passed down through the ViewController chain, and if you never create the parent-child relationship between ViewControllers this system breaks down.
I follow the pattern of (psudo code)
//given:
ViewController *a;
ViewController *b;
[a willMoveToViewController:b];
[b addChildViewController:a];
[a didMoveToViewController:b];
[b.view addSubview:a.view];
I think one of the reason may be the ownership of your childViewController.
If you alloc init it in a function, it is an auto parameter inside that function. And when the function returns, your childViewController will be automatically released, and become nil.
Meanwhile, the view (doohickey.view) of your childViewController, due to you addSubview to self.view, so it will kept by self (viewController). The view still exist in spite of its VC is already nil.
So addChildViewController let self (viewController) keep doohickey (subviewController), therefore the ownership of doohickey and doohickey.view will be unified, and will be released at the same time (after self being released).

how to call (void)viewDidUnload function ? it not calling when I move to other view-controller

enter code hereI have two view-controller.when i move to one view controller to another view controller,(void)viewDidUnload function not calling automatically.
so that it was increasing my memory allocation of app.after 2,3 time moving between these view-controller my application getting stuck.
when I use ios7 simulator it not stuck.
how can i release unused memory? how can I call (void)viewDidUnload function automatically when i move to second view controller?
button click event
[self performSegueWithIdentifier:#"linktoviewmeetingitem" sender:tableView];
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"linkquicksearch"]) {
ViewControllerSearch *searchScreen = [[ViewControllerSearch alloc] initWithNibName:#"ViewControllerSearch" bundle:nil];
searchScreen = (ViewControllerSearch*)segue.destinationViewController;
[searchScreen setSearchString:_txtSearch.text];
searchScreen = nil;
}
}
This method is deprecated in iOS6 ... also even back then viewDidUnload is only called when your viewController is deallocated, not during/after a transition to another view controller. - You are instantiating an already instantiated viewController each time you 'transition' - leading to your memory issue.
You need to wrap the [alloc init] of your viewController in an if statement to check if said viewController instance already exists.
From the docs:
viewDidUnload
Called when the controller’s view is released from memory. (Deprecated in iOS 6.0. Views are no longer purged under low-memory conditions and so this method is never called.)
- (void)viewDidUnload
Discussion
In iOS 5 and earlier, when a low-memory condition occurred and the current view controller’s views were not needed, the system could opt to call this method after the view controller’s view had been released. This method was your chance to perform any final cleanup. If your view controller stored separate references to the view or its subviews, you could use this method to release those references. You could also use this method to remove references to any objects that you created to support the view but that are no longer needed now that the view is gone. You would not use this method to release user data or any other information that cannot be easily recreated.
In iOS 6 and later, clearing references to views and other objects in your view controller is unnecessary.
At the time this method is called, the view property is nil.
Just because you navigate away from the viewcontroller, it won't be unloaded. if you want to catch the event when the viewcontroller becomes navigated away from, use the
- (void)viewWillDisappear:(BOOL)animated;
function if you want to catch it before happens or the
- (void)viewDidDisappear:(BOOL)animated;
if you want to catch the event after.
Basically on the iOS7 and ARC never people use a - (void)viewDidUnload{}
but you can use to force the memory unlock in this mode example:
in your .h have a IBOutlet like this:
#property (strong, nonatomic) IBOutlet UISlider *progressSlider;
than in your .m file you can use:
- (void)viewDidUnload {
[super viewDidUnload];
[self setProgressSlider:nil];
}
Hope this help you

Dangers of Unconventional UIViewController Usage

I'm going through some old code and trying to detect some hard to find bugs. I came across an unusual usage of a UIViewController where the controller is allocated, stored in a property, and its view is added as a subview instead of presenting the entire controller.
Let me start off by saying I know this is kind of hacky and abnormal. That said, what are the dangers in the following implementation? Are there any unexpected side effects that this could cause? What if MyOtherViewController unloads its view and recreates it due to receiving a memory warning at some point, could that cause any strange behavior?
#interface MyViewController()
#property (nonatomic, strong) MyOtherViewController *otherVC;
#end
#implementation MyViewController
- (void)viewDidLoad
{
self.otherVC = [[MyOtherViewController alloc] init];
[self.view addSubview:self.otherVC.view];
}
#end
What you are doing is creating custom view controller containers. This is not a bad thing, but you aren't doing it the way you're supposed to. There is a section in UIViewController's class reference that explains exactly how to accomplish what you're trying to do.
Take a look at Displaying a View Controllers Contents Programatically
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/UsingViewControllersinYourApplication/UsingViewControllersinYourApplication.html#//apple_ref/doc/uid/TP40007457-CH6-SW8
Note this: Important: Never install the view controller’s view into a view hierarchy directly.
I just tracked down a nasty BAD EXEC crash in a project I moved to (see nasty bug below).
I can say that using a UIViewController is very bad because:
You have to make sure the controller is not deallocated. The view won't because it is in view hierarchy with a superview, but the controller has no object to retain it. If it was added to the window as a rootController, to a tab controller, a navigation controller or presented by another controller (normal usage) it would be ok.
It won't receive orientation changes and messages that you would expect to get called besides viewDidLoad.
Nasty bugs. For instance in iOS 5 if this controller is not deallocated before you dismiss a modal controller you'll have a BAD EXEC crash that will drive you crazy. It seems the animation methods from the SDK expect your view controller to be present during the dismiss modal animation.

How to fix view retain cycle causing subview's dealloc to not be called?

I have a view controller which instantiates a bunch of UIButton subclasses and adds them to its self.view and also to a mutable array. These subclasses in turn have a retain property which points to another view. In most cases, the view property points back to the UIButton subclass' superview (the view controller's self.view to which they were added). But not always and not necessarily, which is why I am using this property and not the inherited superview one.
The problem I'm having is that when the view controller's dealloc does:
- (void)dealloc
{
[UIBUttonSubClassesArray release];
[super dealloc];
}
the UIButton subclasses' dealloc is not being called. So the additional release for the view property in these subclasses doesn't get called and, even when the view controller is dealloced, I'm leaking the view controller's view once for each of these UIButton subclasses.
But, if instead I make the subclasses' view property an assign, so that I need not call release in their dealloc, their dealloc does get called and even when there's no code referencing the view now, the app crashes.
Any ideas?
If your viewcontroller is not in the retain cycle, you can break the cycle in its dealloc (and in its viewDidUnload too) by setting your extra retain properties in your buttons to nil.
Although it is always preferable not to create retain cycles in the first place, I don't see enough clue in your question on how they should be avoided in your case.

Resources