ARC not releasing live bytes - ios

Questions about the general model of how I should be programming in iOS.
I started before ARC, doing manual memory management. I was originally taught to make every class variable a property and release in dealloc. This model works great, when I push and pop navigation controllers LIVE BYTES alloc and dealloc respectively.
When I switched to ARC however this is not the case. My live bytes never seem to go down, even when popping a navigation controller. I don't get it, am I not supposed to use properties? I generally use a strong property for anything except for an IBOutlet in which I case I'll use a weak property.
Is there something I'm missing? Do I need to do something in viewDidUnload or implement my own dealloc method???
If I use my app for long enough, I'll eventually receive a memory warning and crash. So I know something's not right.

Related

Xcode Instruments can't detect retain cycle for strong delegate type?

I just spent an hour trying to fix a retain cycle in my code. It was basically the view controller not getting deallocated after dismiss.
However, when I was using Instruments to check for memory leak, it passed every leak check. Please see the image below.
The problem was when I declared the class protocol, I forgot to mark the delegate as weak...But how come Instruments failed to notice this retain cycle?
I'm quite new to Memory management, if my question is dumb, please understand. Thanks. :)
Instruments detect leaks in fairly simple way - if there are no references leading to the root components (say Application Delegate) from an instance, it means that the instance and all instances retaining it are a memory leak, just like Garbage Collector. So if, say, your parent ViewController merely retains another ViewController (which should have been released getting back), and the parent ViewController is still retained itself by any class that is in chain of references to the root components, it is not considered leak.

Is there a way to be alerted when a view controller or any object for that matter is released because of a retain count of 0 in ARC?

There used to be dealloc but since ARC that's gone.
I need some way to be alerted of exactly when an object is freed (and I'd rather not use Instruments since it's really slow and just not working for me right now.)
That's not correct, the method dealloc of NSObject still exists.
See Docs here.

Disadvantage of using strong/retained for IBOutlet?

I have read several Q&A and documentations which state that we should use weak for IBOutlet unless it's top level objects from File's Owner.
But if I still use strong/retained, is there any major downside, or is it just redundant because the subview is already retained with addSubview:?
Note: please do not copy definition of weak / strong here, I don't need that, I want to see real world cases where using strong for IBOutlet could cause problems. Thanks.
With MRC, if you use retain, you will have to release the memory by yourself.
With ARC, if you use strong and the system requests memory from your app (= your view will be unloaded), you will have to release the memory by yourself (note that the controller would be still be active, so no dealloc called there)
For most outlets, weak/assign is appropiate because you don't need to care about releasing the memory.
Exceptions:
IBOutletCollection must be strong/retain. The collection (NSArray) is not retained by the view hierarchy.
You add/remove views dynamically. If you want to remove a view from your view hierarchy and use it again later, the view must be retained somewhere, otherwise it gets deallocated at the time of removal. However, note that you can always retain it in code at the time of removal.
I will mark this as "accepted" until someone provides a better answer.
Apparently the only downside is that when your view receives a memory warning, it would unload the view, and optimally all the subviews should be released. But since your controller still retains them if you use strong, you will have to nil them out manually in viewDidUnload.
From iOS 6, view is not unloaded upon receiving memory warning, so this becomes inconsequential. From a practical point of view there is no major difference between using weak or strong for IBOutlet afaik, unless you have to unload your view manually in your application.

How should I tidy UIViewControllers when they need to be destroyed

I'm starting to write the second version of our iPhone app and I'm trying to tidy up previous mistakes (as it's my first attempt at Objective-C). My question is related to "things I need to do when a UIViewController is destroyed", there seem to be a few contradictory answers out there and I want to make sure I understand correctly.
Couple of constraints:
This code is for use with iOS 5 and iOS 6 devices
I don't wish to register and deregister NSNotifications on viewWillAppear and viewWillDisappear because the UIViewControllers need to receive notifications even if they can't be seen by the user.
I'm using a StoryBoard rather than separate nib files.
So considering the above constraints, are the following statements true?
IBOutlets connecting the storyboard to the UIViewControllers should be weak, the strong reference will be created behind the scenes.
Because the IBOutlets are weak I shouldn't need to nil them out in low memory situations
I shouldn't use viewDidUnload because it's being deprecated instead I should use didReceiveMemoryWarning. In this situation I only need to nil out strong properties (that can re-calculated)
It's acceptable to register for NSNotifications on viewDidLoad.
Because I wish to continue receiving notifications when the view is hidden, the best place to unregister them is in dealloc, there's no benefit in also unregistering them in didReceiveMemoryWarning.
Thanks for your help,
Dan
IBOutlets connecting the storyboard to the UIViewControllers should be weak, the strong reference will be created behind the scenes.
No. NSKeyedUnarchiver (NSCoder) does not change the storage qualifiers associated with user-created outlets. You keep them weak because you don't ever explicitly alloc and init IBOutlets, therefore you do not "own" them in the cocoa sense of the word.
Because the IBOutlets are weak I shouldn't need to nil them out in low memory situations
Not true at all. Zeroing weak references zero out in dealloc, not in low memory situations. Apple expects you to do that by explicitly releasing strong outlets to handle memory warnings.
I shouldn't use viewDidUnload because it's being deprecated instead I should use didReceiveMemoryWarning. In this situation I only need to nil out strong properties (that can re-calculated)
Yes, but as for the replacement for -viewDidUnload, dealloc serves that purpose.
It's acceptable to register for NSNotifications on viewDidLoad.
Because I wish to continue receiving notifications when the view is hidden, the best place to unregister them is in dealloc, there's no benefit in also unregistering them in didReceiveMemoryWarning.
Absolutely.
I believe what you say is correct. Regarding IBOutlets and strong/weak references, see this thread.
As for notifications: unregistering them in didReceiveMemoryWarning seems pointless as they are not by themselves freeing any memory upon de-registration. Therefore you're right in deregistering them upon deallocation.

Receive memory warning and memory leak

I'm using ARC (automatic reference counting).
Is it ok if I set the IBOutlets to nil in the viewDidDisappear instead of viewDidUnload?
Such as these:
[self setTheImage:nil];
[self setBTNplay:nil];
[self setBTNstop:nil];
I'm writing a navigation based app which includes pageViewController, I tested my app in Instruments to see the memory leaks and I keep getting receive memory warning message.
I've even put a log code in the viewDidUnload method. But it doesn't seem to get called when I even pop to rootViewController!
One more thing: If each page has an audioPlayer, where should I set a #property (nonatomic, strong) AVAudioPlayer *audioPlayer; to nil?
Or how do I set it to weak instead of strong? Because it gives me a 'warning' then in this code line:
_audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:poemURL error:nil];
it says: Assigning retained object to weak variable
You don't need to nil out those values in viewDidUnload. Make sure you're using weak properties instead of strong or assign for IBOutlets. Received memory warning doesn't necessarily mean you're leaking. Received memory warning means that your app is consuming too much memory. Run Instruments and edit your question with how much memory your app uses.
The fact that you're using AVAudioPlayer makes me thing that maybe you're pulling down some massive audio files into memory.
Also by the way, initWithContentsOfURL:error: will get you rejected from the App Store because you're blocking the main thread. Try testing your app on an iPhone with only cellular enabled and go into a part of your office/house that has a bad internet connection. Also try with your phone switched to airplane mode. Your app will undoubtedly either freeze for a long time before the connection fails or it will simply crash.
Instead, you should be using grand central dispatch or downloading it via NSURLConnection's block or delegate methods.
First, do not set to nil your properties in viewDidDisappear cause your view is still loaded. You must always set them to nil in viewDidUnload. It's invoked in low memory situations and here you must clean-up all stuff that breaks system memory.
Apple's UIViewController reference for viewDidUnload
When a low-memory condition occurs and the current view controller’s
views are not needed, the system may opt to remove those views from
memory. This method is called after the view controller’s view has
been released and is your chance to perform any final cleanup.
Second , take a look at this tutorial where is explained very well ARC
Are you calling [[NSNotificationCenter defaultCenter] removeObserver:self]; from one of your view controller subclasses? If so, that would explain why you're not getting viewDidUnload called.
If that's the problem, you should remove yourself from specific notifications when needed rather than all notifications as above. (It's OK to call removeObserver:self from dealloc, though.)
Is it ok if I set the IBOutlets to nil in the viewDidDisappear instead
of viewDidUnload?
There are many things wrong from this statement.
First of all, you do not set IBOutlets to nil in viewDidDisappear. viewDidDisappear is called when the view "disappears" (e.g. when it's in a tab bar controller, and you switch to another tab; or it's on a navigation controller, and you push something on top of it); the view can then "appear" again without loading again. If you set IBOutlets to nil, they will not be set again when you appear. (They are only set when the view is loaded.)
Second, if you have a leak, and setting stuff to nil "fixes it", that means you are not releasing the instance variables. You must always release retained instance variables in dealloc.
I've even put a log code in the viewDidUnload method. But it doesn't
seem to get called when I even pop to rootViewController!
Yes, viewDidUnload is only called in low memory situations. It is not called normally in most situations. If you were depending for it to be called, you were using the wrong method.

Resources