Avoid Autoreleasing
It's very common to use autorelease objects for temporary values. For example, if you're creating a temporary string, you might send a +string message to NSMutableString and let the autorelease pool clean up after you.
This design is very simple, but it has some overhead. Autoreleased objects are added to the current autorelease pool. They're not destroyed until the autorelease pool is destroyed, typically at the end of the current run-loop iteration. At this point, the autorelease pool has to iterate through its list and destroy every object. If you add a lot of objects to an autorelease pool, it will need to allocate more memory to store all of the references, wasting space.
In general, you should only use autorelease objects when you need to pass them up the stack. I use the following macro and accompanying function to avoid the need to write lots of custom cleanup code (it's part of the EtoileFoundation framework):
__attribute__((unused)) static inline void ETStackAutoRelease(void* object) { [*(id*)object release]; } #define STACK_SCOPED __attribute__((cleanup(ETStackAutoRelease))) __attribute__((unused))
To create a temporary object that's cleaned up automatically, do something like this:
STACK_SCOPED NSMutableString *buffer = [NSMutableString new];
When the buffer variable goes out of scope, the compiler will automatically insert a call to the cleanup function. This call will then be inlined, so you just get a -release message sent to the buffer when it goes out of scope. The really nice thing about this technique is that it's exception-safe; the cleanup code runs even if the variable goes out of scope as a result of an exception being thrown.
This system ensures that the variable is destroyed as early as possible, which helps keep your memory footprint small. It can also help to improve malloc() and data cache performance, by quickly reusing the same chunk of heap memory, although this is much harder to measure.