Sep 19 2010
Today I ran into a memory error in the iOS application I am working on. The event had special significance for me because I think Clinton was president the last time I saw an actual access violation. C# .NET programmers enjoy the benefits of a managed execution environment, and we don’t see those things much any more. I remembered them from years of C and C++ programming, and I knew in general what to look for, but figuring out specifically what patterns represented risks in Objective-C required reading up on how memory management is handled for Cocoa apps running on iOS.
The answer is that it ain’t much handled at all. The scheme relies on manual reference counting using messages like retain, release, and autorelease, and there are a number of rules for when you should send these messages to a given object. One blog post I read called this a “convention-based” regime, which is a good term for it. The important thing when adhering to a convention is to understand what the rules are. Fortunately the rules for Objective-C aren’t numerous or difficult to understand. In a nutshell: if you own an object you must release it. The corollary might be: if you release an object it better be one you created, or one you have taken ownership of by sending it a retain message. The whole thing rests on knowing whether or not you own an object. One of the places where this gets tricky, as old C++ hands will have no difficulty believing, is in property sets and gets.
Fortunately the rules here aren’t all that obtuse either, and they boil down to: when you set a property release the current value, and retain the new value. There are some variations, but in general that’s what you’re going to want to do. Code that gets the value of a property should adhere to another rule: when you receive an object from a method not prefixed with alloc, init, or copy, or when you are passed an object as an argument to a method call, you don’t own it. You don’t release it unless you have first called retain on it. If everybody plays by those rules then everything works fine, but of course that’s always been the rub with primitive languages: voluntary compliance.
In any case, our code did appear to play by those rules, and yet it was still crashing. The scenario was a simple one…
@interface SomeClass int someProperty; @end @property (retain, nonatomic) int someProperty;
The @property syntax tells the compiler to generate accessor methods named ‘someProperty’ and ‘setSomeProperty’ that adhere to the rules for setting and returning properties. The ‘retain’ and ‘nonatomic’ attributes are instructions to the compiler on how to generate those accessors, in this case adding code to release/retain in the setter, and not adding code to guarantee transactional atomicity. Based on what I could see we were adhering to best practices in using the @property syntax to implement our properties. And yet… it was still crashing.
The line that was faulting looked like this…
self.anotherProperty = someProperty;
Veteran Objective-C programmers will already see a problem here. The line above was faulting trying to read someProperty. The value of someProperty had been set earlier in a line that looked like this…
someProperty = aStringWeWerePassed;
Veteran Objective-C programmers may actually be laughing audibly at this stage of the discussion. The problem with these two lines of code is obvious to them, but it’s the kind of thing a C# programmer would blow right by without a second thought. Naturally, if you define a property and then assign to it, the setter method is going to be invoked. Right?
Uh, no. I’ve been learning as I proceed deeper into this thing that the best way to grok what is going on in Objective-C is to let the old C ethos flood back into your soul. If you never had a C ethos, because you never worked in C, may your deity of choice aid you. In ‘Old C’ the compiler knows diddly squat about properties, and it doesn’t know when to turn a simple and obvious assignment into a call to a method. It doesn’t know unless you tell it, and the way to tell it is like this…
self.someProperty = aStringWeWerePassed; // or [self setsomeProperty:aStringWeWerePassed];
Either of these will work, but what we were doing was a simple assignment of one pointer value to another, without going through the setter method, and without calling retain or release or any of that stuff. Specifically, the reason the program was crashing is that when you are passed an object in a method (in a callback from NSXmlParser in this case) the object is guaranteed to be around until the method that passed it to you returns. If you want to keep it longer than that you call retain on it. That’s what happens in the property setter, except that as you know now we weren’t going through the property setter when we set the value. By the time we read that value later the memory had been deallocated.
Objective-C, as I have grown fond of saying, actually is your father’s programming language.
3 responses so far