Invalidating an NSTimer in dealloc - ios

Following this question, and more specifically, this comment:
because retain (aka strong reference) cycles in the common case where the timer's target is also its owner
I am wondering why dealloc isn't a good place to invalidate an NSTimer.
I remember profiling my app without auto-repeating NSTimer invalidation and then with invalidation in dealloc, and the memory correctly freed.
Is dealloc working differently in the latest iOS?
Isn't in fact your overridden dealloc called prior to any NSObject deallocation? What is dealloc even used for, then? If not manually deallocating the respective object's properties?

ARC will only release ( and call dealloc ) objects, when there are no strong references pointing to this object ( no one is retaining ).
NSTimer creates strong reference and it will retain target.
This means, dealloc will not be called, because NSTimer still has strong reference to the object. If there is no dealloc, this means NSTimer will never be invalidated ... leads to memory leak or even crashes.
There is a way to invalidate timer in dealloc or when target becomes nil. Have a look at the answer here.

Related

UIViewController and NSURLConnection

I create and launch a NSURLConnection in UIViewController's viewDidLoad. And UIViewController is set as a delegate for the NSURLConnection.
What will happen if UIViewController destroys (deallocates) before NSURLConnection has finished its work? It will try to call a delegate, I guess, and the app will crash.
Previously (before using ARC), I used to remember NSURLConnection by a strong reference and cancel it in UIViewController's dealloc method.
Should I do something similar when using ARC. And if not, why?
Why wouldn't you do the same thing when using ARC? If this was important to do before ARC, it is important to do after ARC. Memory management is memory management. The danger of a message being sent to a delegate that no longer exists is exactly the same. It's all the same, ARC or no ARC — except that under ARC, retain and release are being called for you.
NSURLConnection must be storing some internal reference of connection object that why it wont be crashing. Personally i have been keeping strong reference as we don't know how actually internally sdk save these references and this behavior may also change with sdk.
You can avoid crashing when viewcontroller dealloc before connection object by setting delegate method to nil and calling cancel method on the instance

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.

iOS non-zero reference count in ARC and cannot dealloc

Hi All
I am trying to dealloc a ViewController in ARC mode.
However, the RefCount is always non-zero.
I have tried to set all object to nil and all subviews to removeFromSuperview + nil;
and timer to invalidate + nil;
still the counter = 2;
Is there a way to trace which pointer is still in retain?
Thanks
If you are using blocks you might also create retain cycle there. E.g. a block is referenced by an object and inside this block you are referencing object or calling instance method for object.
Another option for retain count not dropping to 0 is that you have registered abject as observer for notification.
You might find this answer helpful:
https://stackoverflow.com/a/12286739/2261423
Example of strong reference cycle from apple docs:
self.block = ^{
[self doSomething]; // capturing a strong reference to self
// creates a strong reference cycle
};
#Billy, why are you doing this? You may not worry about deallocation when using ARC. Controller will be deallocated automatically, when there will be no references to the controller. Yes, views do not refer to controller, they are referred by it! So removing that views will not affect retain count of the controller. If you really want to remove View Controller from memory, remove it from parent view controller and set all links to nill.

iOS memory is not being released, background thread

I have two View Controllers, one with a grid of buttons, and the second one is a detailed description based on the button the user presses.
I'm using "Mark Heap" in Instruments allocations tool and finding that my app is increasing in memory after the user sees the detailed View Controller and then presses the back button in the navigation bar. IT should result in a net change of zero for memory... I'm using ARC, xcode 4.2.1, deploying to ios 5.0+
When I load the new ViewController after the button press, I load some data images in a background thread. Is it possible that because I'm pressing the back button quickly, that data is still being loaded in the background thread and is never released from memory?
from the slides from the class i had that described memory cycles problem you may be facing:
what if you had the following property of a class?
#property (strong, nonatomic) NSArray* myBlocks; // array of blocks
and then tried to do the following in one of that class's methods?
[self.myBlocks addObject:^() {
[self doSomething];
}];
all objects referenced inside a block will stay in the heap as long as the block does. (in other words, blocks keep a strong pointer to all objects referenced inside of them.)
in this case, self is an object referenced in this block. thus the block will have a strong pointer to self. but notice that self also has a strong pointer to the block (through its myBlocks proeprty)
THIS IS A SERIOUS PROBLEM!
neither self nor the block can ever escape the heap, now. that's because there will always be a strong pointer to both of them (each other's pointer). this is called a memory "cycle".
solution:
local variables are always strong. that's okay, because when they go out of scope, they disappear, so the strong pointer goes away. but there's a way to declare that a local variable is weak. here's how ...
__weak MyClass* weakSelf = self;
[self.myBlocks addObject:^{
[weakSelf doSomething];
}];
this solves the problem because now the block only has a weak pointer to self. (self still has a strong pointer to block, but that's okay) as long as someone in the univese has a strong pointer to this self, the block's pointer is good. and since the block will not exist if self does not exist (since myBlocks won't exist), all is well!

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