This is an excerpt from a message I sent to Cocoa-Dev - I just realised I forgot to Reply to All and thought this info might be handy for someone googling for help:

Cocoa memory management is fairly simple: You have alloc/init to create an object, and release to dispose of it again. If you get an object someone else created, you can call retain or copy on it to make sure it stays around until you don't need it anymore, which you indicate by calling release. There are several variants of init and copy, like initWithData: and mutableCopy, but for those the rule is the same.

However, often, people check the retainCount of their objects and try to add as many release calls as are needed to bring the retain count back down to zero. There's a subtle mistake in there. You're not supposed to worry about the retain count, you only have to worry about matching up your alloc/init, copy and retain calls with that many releases. Why? Easy:

The whole point of retain is that several objects can share access to another object without having to know. The retain count counts all owners, and thus can only be used as a hint when debugging if you know who the other owners are and how often they retain an object with 100% certainty. Which you never do. [1]

Let's assume you have an NSScanner and you're passing it a string you created. In this case, the scanner may be retaining your string, and since scannerWithString: contains neither retain, nor init, nor copy, you can assume that it is owned by someone else (like the current autorelease pool). So, that the retain count reaches 2 is probably due to the scanner holding on to its string.

When you release the string a second time to make its retain count hit zero, what you effectively do is kill the text that the scanner still expects to be there. When the owner of the scanner releases the scanner, the scanner will in turn perform its duty and try to release the string. Since you already released it, the scanner is talking to a zombie object, and your program will generate a bus error or bad access signal (if you're lucky -- if you're not, the memory where your textViewContents used to be hasn't been reused yet and still contains the old values and your code will just start behaving oddly ... which is the most engaging kind of bug to track down).

So, match up your calls, don't mess with the retain count.

1) Yes, strictly spoken there are situations in which the other owners can be known, but the number of retains on an object may change from system version to system version, and thus it is very unhealthy to rely on that. And it's easy to think you know all owners and then realize when your app crashes that you overlooked some delegate or internal object that does an additional retain.