Sunday, July 13 2003 @ 05:41 PM UTC
Contributed by: cocoa
please, some advice about good retain/release strategy…
If you allocated an object using one of these routines, you are reponsible for releasing it since the object will have a retain count of 1 AND will NOT be added to the current release pool:
alloc, allocWithZone, copy, copyWithZone, mutableCopy, mutableCopyWithZone, new
If you allocated an object using one of the utility class method its class provides (like [NSArray array]), the object has already been added to the current autorelease pool, and as such will be released for you.
The common errors here are:
- adding yourself the object allocated with a class utility method, to the current pool: as stated in the Apple documentation: An object can be put into the same pool several times and receives a release message for each time it was put into the pool. So adding an object to an autorelease pool several times works, at condition to retain it as many times…;
- forgetting to retain an object returned by a class utility method, often assigned to an instance variable of some other class;
These two common errors are easily spotted since most of the time they generate a SIGNAL. (but not always…)
- retaining one more time, an object returned from one the above methods (alloc, copy, …) and releasing it only once;
- retaining objects more than needed and forgetting to release when appropriate - often the result of poor error handling routines;
- forgetting to release non-autoreleased object after adding them to a collection: collections like NSArray, NSSet, NSDictionary,… retain the objects they store, and release them when deallocated.
- retaining NSTimer object: NSTimer created by convenience methods are autorelased and retained by the run loop in which they are registered. The run loop will release it at invalidation time.
These lead to memory leaks and are more difficult to spot.
Be careful with shareware frameworks: always check that they respect the "retain count of 1" rule for their init methods and the autorelease feature for their class convenience methods.
One more subtile source of trouble: reference loops with retained instances.
If an object of class A has an instance variable holding a reference on an object of class B which in turn also has a reference to its owner of class A, you should definitively be very careful on deciding who from class A or class B should retain instance of the other class and avoid that both classes have instance variables retaining object of the other class. The order of deallocation of your data structure should dictate who retains who: in general father retaining sons - assuming father are released before sons (you should overwrite the dealloc of the father and release the sons there). Sons may hold reference on father for efficiency of some algorithm implementation but should not retain them. Of course, this rule is general and specific case may require another policy…
Eventually a little note about set--- accessors:
is one of the correct way to write a set accessor: by using autorelease instead of release, you are covered against the problem that will appear in case myNewInstance and myInstanceVar reference the same value and the retain count of the object is 1. In which case, the first release will really end-up by calling dealloc However you should still examine if the parameter should be copied (or mutable-copied) instead of just retained. This could be necessary with classes existing in mutable and non-mutable versions…
Another version could be:
What's "wrong" ? It is not thread save !
A thread switch between the [myInstanceVar release]; and next line is susceptibe to sigtrap randomly if the interrupting thread calls getMyInstance.
Another topic you will often encounter in discussion lists is what's the recommanded way to write the get accessor:
What's the difference ?
The second one allows the caller to get an instance variable of a container object, dispose of the container and continue to play with the instance variable until the next release of the current autoreleased pool, without being hurt by the release of the instance variable indirectly generated by the release of its container:
The first form is a little bit more efficent in term of code execution speed.
However, if you are writing frameworks to be used by others, maybe the second version should be recommanded: it makes life a little bit easier to people using your framework: they don't have to think too much about what they are doing…;) If you choose the first style version, state it clearly in your documentation…
Whatever way you will be choosing, remember that changing from version 1 to version 2 is save for client code, when going back from version 2 to version 1 will break existing client code…
Another example: how to write a "pop" method: