beware singletons and autorelease

Wed, Mar 17, 2010

I’ve been delving into iPhone development on a small project to develop an app that lets me see what’s happening on the summit of Cairngorm, using the data from the automatic weather station up there. The app, which I’ve codenamed iAWS at the moment, has a few screens for data viewing graphically using core-plot as well as textually from the summit station. For example, here’s the plot of the average temperature on the summit:

Average temperature on iAWS

and the latest reading parsed from the XML feed:

Latest conditions on iAWS

It’s been a hoot to write so far, with lots of brain expanding stuff to get to grips with, getting my C/C++ head back on but I hit a particularly nasty problem when I added the text screen. The summit data is downloaded and parsed by the AWS class, which is basically the Model in MVC and I thought it would be handy to have it as a Singleton, accessible from anywhere in the app. So I read this interesting article on turning a class into a Singleton and used the SynthesizeSingleton.h file provided and that let me do this anywhere in the app:

AWS *aws = [AWS sharedAWS];
all well and good, until I added that second view. The Singleton macro only ever calls alloc/init on AWS once and that’s where all the downloading and parsing occurs but after going to the text screen, opening the graph screen then caused the app to crash and the weird thing was, most of the AWS variables were out of scope, i.e. not there, gone, disappeared. The arrays holding the reading data were still there and usable though. And here’s why.

I was using this to store the various readings:

NSString *endDate;
endDate = [NSString stringWithFormat:@“%@”, date];
but it turns out that stringWithFormat returns an autoreleased NSString. So when you do this:
-(void) doSomething {
  AWS *aws = [AWS sharedAWS];
  … aws.startDate …
it’s fine the first time you do it. The Singleton macro causes this to run:
[[AWS alloc] init]
and everything is parsed and stored in the AWS class. But then, aws in doSomething goes out of scope and the garbage collector bins all those autoreleased NSString objects holding all the data. The way round it is to take control of memory management:
NSString *endDate;
endDate = [[NSString alloc] initWithFormat:@“%@”, date]];
but don’t forget to call:
[endDate release]
or you’ll leak memory like a sieve.

comments powered by Disqus