Tuesday, March 13, 2012


For NSURLConnection errors, you might want to read my post here:

Once a while we experience our app getting crashed, and Instruments/Zombies/stack trace debugging etc. leads us to nowhere fruitful.
Stack trace in this will simply show us direction, and we need to follow some simple yet critical concepts in mind to make this debugging easy, interesting and not to be mentioned, avoiding in future codebases :-)

Any object DOES NOT get d'alloced if it is owner of some property, or has some references pointing to it.
If it does get dealloced, then it causes "dangling pointers" to remain in memory, and at any later stage of progression, any callbacks received to these pointers causes an EXEC_ BAD _ ACCESS or OBJ_ MSG _SEND errors, which are quite pain to debug.

This is also true for UIViews, if they have a superview, then the parent "retains" its subviews, unless the subview is not removed from its view hierarchy( When a UIViewController is popped, it automatically clears its view hierarchy).

The solution?

Whenever a reference is made to an object which you know will have to be released later, make sure the references are removed in a proper pipeline.
One example is a NSTimer, or a CLLocationManager object.
If your code has implemented a NSTimer, the iOS sets a delegate to your object, and unless the timer is "invalidated", the delegate is "retained" by the iOS, and hence not freed.

Another example is, when you create custom protocol references, which are simply used to handle post callbacks for Objective C protocols( eg. UIWebViewDelegate, CLLocationManagerDelegate, etc.)
Since these are "assigned" to your classes, unless the delegates are assigned "nil", the respective class instance DO NOT get freed, hence causing crashes.

Let me clarify this by giving an example.
You have a navigation hierarchy, with two view controllers, FirstViewController(FVC) & SecondViewController(SVC), with FirstViewController being the rootViewController.

You "push" an instance of SVC, and you can "pop" it whenever you want.
Now SVC has an instance of another class, CustomLocationManager(CLM), which is simply an NSObject to receive CLLocationManagerDelegate callbacks(and do some extra magic :-)).
So your CLM header class looks like this:

@interface CustomLocationManager : NSObject {
NSTimer *locationRefreshTimer;
double latitude,longitude;
CLLocationManager *locationManager;
id delegate;

Now inside SVC, this instance of CLM will keep on refreshing location changes, and update its "delegate", which in-turn is your SVC itself.

@interface SecondViewController : NSObject < CustomLocationManagerDelegate > {
CustomLocationManager *customLocationManager;
//your impl.

Ideally, when your SVC instance gets popped, this will be your d'alloc hierarchy:
SVC popped (SVC d'alloced)-> CLM d'alloced -> CLLocationManager dealloced.

Interestingly, now press CTRL + I( to launch Instruments), and choose "Leaks" or "Allocations", now again run your code, pop the SVC, and observe your allocations. Search for "CLLocationManager", and you'll find it in allocations!
This means, although your SVC was popped, CLM instance & CLLocationManager object are still living since their references have not yet been cleared.

Now, if locationmanager gives a callback in CLM with an updatedLocation, and your CLM tries to further deliver a callback to SVC indication "locationUpdated:", the line inside CLM will be responsible for the apparent crash in your code, since SVC instance has been d'alloced, but its delegate reference inside CLM has not been nullified, and results in a dangling pointer.

The correct way of d'allocating callbacks will be as follows:

SVC release -> set CLM delegate to nil-> inside CLM, invalidate all timers, and setCLLocationManagerDelegate to nil.

This causes now the following hierarchy:

[SVC release] -> [CLM release] -> [CLLocationManager release];
Now again after making the above changes, run Instruments.

No comments:

Post a Comment