Wednesday, May 11, 2011

Things I learned while debugging memory leaks in Xcode

It has been one heck of a day. I started doing memory leak testing yesterday on our iOS application. I am not done with the full application but I have just knocked off a large chunk of coding around database access and field editing so I wanted to make sure all of that was clean before I moved on to the next step and forgot how all of this code worked together. Of course I had some memory leaks and they needed to be fixed.

Like everyone I got the point where I was thinking the tool that checks for leaks had a bug. It always seems that way when you look at your perfect code and can't figure out for the life of you why it has a problem. In the end I was able to fix all leaks, the tool was correct.

While searching the web for leak reasons I ran across a better way of doing some things especially temporary string formatting. I was using:

NSString *cmd = [[[NSString alloc] initWithFormat :@"drop table if exists %@", [table tableName]] autorelease];


But the better way to do it with much less typing and bracket clutter is:

NSString *cmd = [NSString stringWithFormat :@"drop table if exists %@", [table tableName]];


The first version is allocating memory, which you must free, using a method on the object while the second version is using a static method to create a string that it owned by something else so you don't have to release it. Probably just marked as autorelease but you don't care about the internals.

I cleaned up all the areas in the code using the first format but one. For some reason in that one place I got a memory leak if I did not use the alloc and release myself. I commented the code so I will not try and "fix" it at some later point.

Next I made a sad rookie mistake that is understandable coming from a Java background. In some of my classes in the dealloc method I called [super dealloc] first. Big no no. Of course in Java you don't have destructors so you almost always call super first because you are constructing. Calling [super dealloc] must be the last thing you do as it cleans up your object and all of its pointers. You are unwinding your construction. I was sitting there watching the program hit my dealloc call and looking at the retainCount (I know, this is not to be trusted) having a value of 1 and not understanding why it was flagged as leaking as I left the dealloc call and wondering why my release calls would crash things. A mistake I am not likely to make again.

With a much better understanding of memory allocation and having read a number of web sites talking about who allocs and who cleans up I revisited the code and found a few places where method names were misleading or the object creating the values was not cleaning them up. I reorganized it all so allocation and clean up happen in same class if at all possible and method names don't look like Java but look like Objective C. There were not many of them but now they are starting to stand out to me.

I missed cleaning up two of my four editing view controllers too. There I was putting nice clean up code in one of them but not all of them which happens when you are writing massive chunks of code in short order in a new language.

Things are looking really good right now. I have run the program and beat on it pretty hard in both the Profile mode and with extra Run Malloc settings enabled. Running totally clean under both. All the databases are in place and my memory mapped databases are working as expected.

I installed Xcode 4.0.2 and so far have not had another kernel panic. Still steamed you have to download such a huge pile bytes overnight for each Apple Xcode update.

No comments:

Post a Comment