views:

304

answers:

3

As is common knowledge, calls to alloc/copy/retain in Objective-C imply ownership and need to be balanced by a call to autorelease/release. How do you succinctly describe where this should happen? The word "succinct" is key. I can usually use intuition to guide me, but would like an explicit principle in case intuition fails and that can be use in discussions.

Properties simplify the matter (the rule is auto-/release happens in -dealloc and setters), but sometimes properties aren't a viable option (e.g. not everyone uses ObjC 2.0).

Sometimes the release should be in the same block. Other times the alloc/copy/retain happens in one method, which has a corresponding method where the release should occur (e.g. -init and -dealloc). It's this pairing of methods (where a method may be paired with itself) that seems to be key, but how can that be put into words? Also, what cases does the method-pairing notion miss? It doesn't seem to cover where you release properties, as setters are self-paired and -dealloc releases objects that aren't alloc/copy/retained in -init.

It feels like the object model is involved with my difficulty. There doesn't seem to be an element of the model that I can attach retain/release pairing to. Methods transform objects from valid state to valid state and send messages to other objects. The only natural pairings I see are object creation/destruction and method enter/exit.

Background: This question was inspired by: "NSMutableDictionary does not get added into NSMutableArray". The asker of that question was releasing objects, but in such a way that might cause memory leaks. The alloc/copy/retain calls were generally balanced by releases, but in such a way that could cause memory leaks. The class was a delegate; some members were created in a delegate method (-parser:didStartElement:...) and released in -dealloc rather than in the corresponding (-parser:didEndElement:...) method. In this instance, properties seemed a good solution, but the question still remained of how to handle releasing when properties weren't involved.

+2  A: 

I wouldn't say that dealloc is where you would call autorelease. And unless your object, whatever it may be, is linked to the life of a class, it doesn't necessarily need to be kept around for a retain in dealloc.

Here are my rules of thumb. You may do things in other ways.

  • I use release if the life of the object I am using is limited to the routine I am in now. Thus the object gets created and released in that routine. This is also the preferred way if I am creating a lot of objects in a routine, such as in a loop, and I might want to release each object before the next one is created in the loop.
  • If the object I created in a method needs to be passed back to the caller, but I assume that the use of the object will be transient and limited to this run of the runloop, I use autorelease. Here, I am trying to mimic many of Apple's convenience routines. (Want a quick string to use for a short period? Here you go, don't worry about owning it and it will get disposed appropriately.)
  • If I believe the object is to be kept on a semi-permanent basis (like longer than this run of the runloop), I use create/new/copy in my method name so the caller knows that they are the owner of the object and will have to release the object.
  • Any objects that are created by a class and kept as a property with retain (whether through the property declaration or not), I release those in dealloc (or in viewDidUnload as appropriate).

Try not to let all this memory management overwhelm you. It is a lot easier than it sounds, and looking at a bunch of Apple's samples, and writing your own (and suffering bugs) will make you understand it better.

mahboudz
auto-/release refers to whichever of `autorelease` or `release` is appropriate in a given context. For `-dealloc`, it refers to `release`.
outis
@outis not necessarily. It is not incorrect to `autorelease` in a `dealloc` method. It's just (usually) smarter to `release` in `dealloc`. `autorelease` is just for saying "I don't care about this object anymore, but someone else might want it soon, so I'll give them a chance to grab it by delaying the release for a bit."
Dave DeLong
It's not overwhelming. I have no difficulty with the notion of retain count memory management, nor with putting it into practice. I could even describe it in a couple paragraphs. The problem is succinctly describing where to release, which I feel is possible. "When" is easy: auto-/release if and only if you alloc/copy/retain. It's more a matter of pedagogy and discussion than anything.
outis
@Dave DeLong: Granted, but my statement about context still holds. My comment was in response to mahboudz's first sentence.
outis
Out of curiosity, I went and counted the `release` s in one of my iPhone projects: 111 total (`release` and `autorelease`) with 57 of them in my various `dealloc` s.
mahboudz
Actually, that should be "who" is easy. "auto-/release if and only if you alloc/copy/retain" is a matter of "who."
outis
@mahboudz: how about setters? Other methods? Outside of `-dealloc` and setters, are any of the released objects not stored in local variables? The answers to this are relevant to the question I ask in the first comment to Rob Napier's answer.
outis
Of course, if I am not interested in an object for more than the duration of my routine, I will be storing that object in a local variable, and releasing that object before the scope of the variable ends, and the object leaks. I think it is a mistake to try to tidily wrap it up in a rule like: "You only auto-/release in dealloc or setters. Or if it is a local variable. Or if you already released in viewDidUnload. Or in didReceiveMemoryWarning. Or when creating children objects fail, and you need to release the parent object so that you don't have a half-created object around. Or..."
mahboudz
@mahboudz: I've seen code that releases objects in clearly the wrong place/at the wrong time. There should be a simple principle that codifies this. Do you assert that no such principle can apply to every case?
outis
+3  A: 

The question isn't where it should happen, it's when.

Release or autorelease an object if you have created it with +alloc, +new or -copy, or if you have sent it a -retain message.

Send -release when you don't care if the object continues to exist. Send -autorelease if you want to return it from the method you're in, but you don't care what happens to it after that.

NSResponder
I've been using "where" because I hope for the rule to identify the points in the source code (which is a matter of "where") to put releases. "When" is in terms of processes, which points out I've been thinking of programs when I should perhaps be thinking of processes.
outis
The second paragraph is just a restatement of basic ownership rule (also stated in the first sentence of my question), but I like where the third is going. It needs something about releasing aggressively, *as soon as* you don't care.
outis
One slight extension I would offer to the third sentence is that you set your reference to nil if you you are sending release and not only don't care if it continues to exist, but expect never to see it again.
Kendall Helmstetter Gelner
+3  A: 

Properties simplify the matter (the rule is auto-/release happens in -dealloc and setters), but sometimes properties aren't a viable option (e.g. not everyone uses ObjC 2.0).

This is a misunderstanding of the history of properties. While properties are new, accessors have always been a key part of ObjC. Properties just made it easier to write accessors. If you always use accessors, and you should, than most of these questions go away.

Before we had properties, we used Xcode's built-in accessor-writer (in the Script>Code menu), or with useful tools like Accessorizer to simplify the job (Accessorizer still simplifies property code). Or we just typed a lot of getters and setters by hand.

Rob Napier
True, properties mostly add support at the language level for a common pattern. Now that I think of it, I usually use accessors when working with members in non-2.0 code. Plus, accessors are more aligned with the object model. Do accessors, combined with matching releases to retains within the same block, cover every case? That is, the rule is 1) auto-/release members in setters and `-dealloc` and 2) auto/-release local variables in the same block they're retained.
outis
Properties mean that many, many people are no longer writing bad accessor methods. That is a major plus.
Kendall Helmstetter Gelner
@outis: These patterns should address every properly written case. The Analyze tools in Xcode 3.2 nicely check for this.
Rob Napier