Jun 2010

Follow up to memory management thing

In the previous post I have an initWithString method that looks like this:

- (id)initWithString:(NSString *)aString {
    self = [super init];
    if (self == nil)
        return nil;
    something = [aString retain];
    return self;
}

I knew, as I was typing it, that I’d get some feedback along the lines that I should use copy instead of retain.

And I did.

And it’s excellent advice, totally, no question. The reason to use copy instead of retain is because it enforces the expected immutability of that incoming string, so the caller is free to change it in case it’s really a mutable string.

However, I don’t follow that advice. One reason is that, in my eight years of Cocoa programming, this has never been an issue. Not once have I run into a problem because I used retain instead of copy in a case like this.

The second reason is that I consider the use of copy here a case of overly-defensive programming. The effect would be to hide a bug, if one exists, and I wouldn’t want that.

Given those two reasons, it’s just bonus that using retain means avoiding the extra allocation that using copy would mean — but I’ll take that bonus.

Update 5:24 pm: Yes, copy returns self for immutable objects. (All the time, at least for Foundation objects? I don’t know.) So, no bonus in those cases. But I myself often pass mutable objects where immutable is expected. So I get the bonus. (And it’s not a bug on my part, no.)

Update 5:41 pm: OK, why do I consider it a bug if aString is really a mutable string and I change it elsewhere? It’s totally legal if aString is really an NSMutableString. I do things like that all the time. But I code as if there’s an implicit contract: any Foundation objects (strings, dictionaries, arrays, sets, etc.) passed between objects must be treated as if immutable. If the object that receives the object wants to do something based on aString, it may, indeed, then have to make a mutableCopy or whatever. And if aString was really a mutable string, the caller has to not change it from that point on.

Why do I code this way? It simplifies things. It removes doubt.

How I Manage Memory

I’ve noticed, in looking at other people’s Cocoa code over the years, that sometimes people still do weird things with retain, release, and autorelease — as if they’re not quite sure on the basics of memory management yet.

So I thought I’d talk about how I do things. There are three important points, then a practical explanation of one of them.

  1. Overall memory use is a design issue. It’s not usually a case of over-using autorelease or something like that. (For instance: are all your objects in memory at all times, or do you use something like Core Data so you don’t have to do that?)

  2. Memory management — the nuts and bolts of making sure you don’t leak or over-release — should be as simple as possible and done in the same way every time.

  3. The exceptions to #2 should be done only as a result of profiling in Shark and Instruments.

Practical explanation of #2

I find not-leaking and not-over-releasing very, very easy. That’s because I have a simple system, and I do things the same way every time, except when profiling tells me I need to make an exception. (Which is rare.)

Here’s what I do:

I use properties, and I use the full form: self.something and self.something = whatever. I try to avoid custom accessor methods, just use the standard synthesized methods.

I don’t use the full form in init and dealloc, though, because it might trigger KVO or have other side effects.

So a made-up class might look like this:

@interface BSThing : NSObject {
@private
    NSString *something;
}

- (id)initWithString:(NSString *)aString;

@property (nonatomic, retain) NSString *something;

@end

@implementation BSThing

@synthesize something;

- (id)initWithString:(NSString *)aString {
    self = [super init];
    if (self == nil)
        return nil;
    something = [aString retain];
    return self;
}

- (void)dealloc {
    [something release];
    [super dealloc];
}


- (void)someRandomMethodThatDoesStuff {
    //Let's just change something
    self.something = @"Something else";
}

@end

Make sense? Outside of init and dealloc, access is always via self.something.

There are two other things I do:

Pretty much create everything as autoreleased

Seriously. I almost never write code like this:

NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
//do something with dict
[dict release];

It’s more code and it complicates things and it’s easy to make mistakes. I get confused. I don’t understand at-a-glance that the code is correct. I know there is advice to avoid autorelease on iPhone — but you already know you can’t avoid it, and I’ve found that any drawbacks by use of autorelease are over-shadowed by other issues.

So here’s what I always write:

NSMutableDictionary *dict = [NSMutableDictionary dictionary];
//do something with dict, completely un-worried about leaking it.
//I can even return early.

Autorelease pools

One exception to the above is, of course, when you’re doing a bunch of allocations in a loop. Then I will use an inner autorelease pool. I won’t do that crazy thing where you make it drain every 10 times or whatever — I’ll drain it in each pass. Simpler — and no reason to change that unless Shark or Instruments says to change it.

I’ve been doing it that way since reading Mike Ash’s article Autorelease is Fast. Though, obviously, it’s not as fast on iPhone, it’s still probably faster than you think it is.

Recap: my simple rules

  1. Use properties, and use synthesized accessors as much as possible.

  2. Always use the self.something form — no direct access to ivars, except in init and dealloc, where only direct access is allowed.

  3. Create temporary objects, or objects that get returned by a method, as autoreleased.

  4. Use autorelease pools as needed, but don’t try anything fancy.

  5. Profile in Shark and Instruments, and make exceptions to the above only when it makes a real difference.

  6. Consider that your overall memory use issues are probably design issues, not (usually) code issues.

Following these rules, writing Cocoa code is damn close to scripting.

Casual

I’m not sure what relative this is, but I love the picture’s Alfred E. Neuman vibe.

All’s good here, yep

Road to beach

My great-grandmother (on left) and her sisters (I think) on the road to the beach in New Jersey. Click thumbnail for bigger version.

Road to beach

Put weblogs on bitbucket

I’ve moved the repositories for inessential.com and ranchero.com to bitbucket and made them public read-only.

ranchero.com repository
inessential.com repository

It’s just the raw stuff that gets rendered into weblog posts — it’s not the rendering scripts. There’s no actual software. Just a bunch of words.

Not sure why I did this. Maybe just because I could. But sometimes big bunches of text can be useful for somebody somewhere.

The actual posts are in the posts directory. Older posts are HTML, newer posts are written in Markdown. The lines at the top of each post that start with a @ character are metadata. Stuff between [[ and ]] are macros. Stuff between [[= and ]] are includes (grabbed from Snippets folder).

I wrote about my system in early 2009 here. I’ve been quite happy with it. To restate the advantages:

  1. Static HTML pages are fast. I don’t have to worry about getting Fireballed. :) And they’re easily portable, if I ever have to change web hosts.

  2. Static RSS feeds are fast too, which is good because they get requested a lot.

  3. Having source control is especially nice when I’m working on templates.

  4. Having source control is great for offsite backups. (I was using Dropbox before I switched to bitbucket. I’ll probably still keep a backup on Dropbox, just because.)

  5. Being able to search through my weblogs on my desktop using Spotlight or ack is very convenient. (Okay, I admit I use ack mostly.)

  6. The system can generate a local preview version, so I can render my site just on my machine and look at it before uploading. I don’t use that often, but it’s nice when working on templates.

Best part: I still get to use MarsEdit — I have a small Ruby-based server that implements the API that MarsEdit expects. I hardly ever actually look at the weblogs as files-on-disk, and I never edit the posts that way (though I could).

Reminder about upgrading NetNewsWire

I don’t usually use my personal weblog for this kind of thing — but it’s important for NetNewsWire users, so I want to make sure all bases are covered.

A couple months ago we released updated versions of NetNewsWire that work with an upcoming change in Google Reader. That change (in the authentication system) goes into effect tomorrow (Tuesday). So it’s important to make sure you have the latest version.

In a nutshell: you should have Mac 3.2.7 or greater (you can look at the About window). For iPhone and iPad, you should just check the App Store to make sure there isn’t an update waiting for you.

More details are in a post on the NetNewsWire weblog.

Practical WWDC tips

No time to write something long or organized this year. Quick notes, then.

Drink plenty of water. Make sure there’s plenty of water in your hotel room.

Try to avoid walking the streets at night alone. I wouldn’t say the streets are dangerous. But still.

Avoid the Tenderloin.

You are your fellow developers keeper. Be inclusive. Help anyone who needs help. Be that good person your grandmother imagines you are.

You’re here to learn and see cool things. Part of that is telling other people about the cool things you’ve seen. (Related: you’re not here to complain.)

Go to the ADA ceremony. When you don’t win (odds are you won’t win), remember that feeling. It’s useful year-round. But clap hard for the folks who do win — they worked hard and earned it, and they deserve your support. If you see them later, congratulate them.

Use your iPhone alarm and the hotel’s alarm clock, especially for Monday morning.

Make it drop-dead easy to plug your iPhone into power at night, so you don’t forget.

Put your hotel room number in your wallet.

Use Twitter for organizing and finding out what’s going on.

Remember to eat. Don’t worry about what you eat too much — you’ll go back to being virtuous when you get home. The important thing is to not skip meals: you’ll need the energy.

Remember to listen to music sometimes. You’ll need the energy from that, too.

You won’t be graded on your week — except by you.

The friends you make will mean more to you than the APIs you learn.

Guy English is there only to break your heart. (Total sadist, that guy. Delights in it. Whatareyagonnado.)

BSSAXTweetParserDemo

I had reason to look at MGTwitterEngine today. It had been a while since I looked at it, and I was reminded that the libxml-based parser in there was originally based on my BSTweetParser from a couple years ago. (Folks did a real nice job of improving it, of course.)

But I don’t do my XML parsing like that anymore. I still use libxml, but I use the SAX interface these days.

Here’s a new demo project — somewhat like the old BSTweetParser, but using SAX: BSSAXTweetParserDemo. (It’s an Xcode project folder with source.)

I have no special unnatural love for SAX, like some people, but it’s the best way to get both high performance and low memory use. (These days I always use SAX.)

Notes about the code

Twitter’s public timeline XML is very simple, so it makes a great demo. Easy to follow what’s going on.

SAX is event-based.

SAX means you don’t have to have the entire XML document in memory — you can pass it to the parser in chunks. This demo doesn’t do that, but my production code does. (Do you want to hold a 5MB XML document in memory on an iPhone? Nope.) Luckily, NSURLConnection makes this very simple. Don’t append data to build the response body — just pass each chunk to the parser and forget it.

The original BSTweetParser code allocated a ton of strings and dictionaries. This new code still has to allocate memory — but it allocates a whole lot less memory. This makes a giant difference on iPhones and iPads.

The original BSTweetParser code just turned everything into arrays, dictionaries, and strings. This new code is selective: it stores only what it wants to store, and it uses custom objects: BSTweet and BSUser instead of dictionaries. I recommend this when performance and memory use matter (as they always do on iPhones and iPads).

The comments in the demo code hint at, but don’t entirely demonstrate, the best way to parse a stream like a stream of tweets: don’t create a big array — instead, each time you have a new tweet, call some delegate to do something with it. No need to store the whole thing as an array and then process it — that uses too much memory. (But that’s exactly what the demo is doing. To keep the demo simple.)

This code not only does less memory allocation, it’s also more than twice as fast as the original BSTweetParser. (The two things are related, of course.) On my development machine, BSSAXTweetParser parsed the timeline in about 0.0008 seconds, while BSTweetParser did it in about 0.0017 seconds. (“Faster and less memory? Sold!”) (I bet the difference is way more dramatic on iPhones, but I built this is a Mac Foundation tool.)

You’ll probably realize fairly quickly what parts of the BSTweetParser class could be made an abstract class from which your real parsers would inherit.

There’s a bunch more code in BSSAXTweetParserDemo than in BSTweetParser. I prefer less code to more code almost as much as I prefer breathing to not-breathing. But that’s life — sometimes the faster and better thing takes more code.

I put this thing in the public domain. Use however and wherever.

In the tradition of all demos ever, there’s no error checking or handling.

When it comes to things like XML parsing on iPhones and iPads, the entire name of the game is doing the least work possible.

There are ways to make this even faster still.

Archive