Finding a leak is fairly simple with OmniObjectAlloc. To see how this works, begin a new mark and select it. Then switch to the example application:
Click on the 'Leak' button. This will execute the following code from OOAExampleController.m:
- (void) leak: (id) sender; { // Leaks the old array array = [[NSMutableArray alloc] init]; }
Since this doesn't release the old value of array, this value will be leaked. After having clicked on 'Leak', there should be a row in the class table in your OOA document called 'NSConcreteMutableArray', among others. Click on the 'Leak' button several more times and notice that the count in the Current (and Peak and Total) will increment after each click.
This demonstrates the easiest type of leak to identify. Repeated invocation of a particular application feature should not cause objects to be created that never get deallocated.
Depending upon the logic in your application, it may be difficult to determine whether such an object will ever get deallocated. In some cases, an object allocated during one invocation of a feature may be kept until the next time the feature is used. In other cases, an object might be kept for a particular amount of time or until some cache threshold is exceeded.
As an example of this, click on the 'Cache Data' button on the example application. For each click, this will execute the following code:
- (void) cacheData: (id) sender; { NSObject *object; // This object will hang around for a while. object = [[NSObject alloc] init]; [object performSelector: @selector(release) withObject: nil afterDelay: 30]; }
This will allocate an NSObject and NSConcreteTimer instance. Upon first inspection, this appears to be a leak. But if you wait long enough (30 seconds in this case), this 'cached' data will automatically get flushed.
Marking is very helpful when attempting to distinguish leaks from cached data. The typical procedure for finding leaks of all of these types is to:
After this point any objects allocated during the mark should get deallocated eventually. You may need to wait for a while, perform the operation again, or perform the operation with different data. If you cannot cause the objects allocated during the mark to get deallocated, you may have identified a leak.
In some cases, it may be fairly easy to determine the cause of the leak by looking at the allocation events for one of the leaked instances. In the case above, each of the leaked NSConcreteMutableArray instances will have a single ALLOC event. By selecting the ALLOC event, you can see the symbolic stack backtrace from whence the event originated. In this case it is fairly obvious that this ALLOC is missing a matching release. As long as the number of events remains low, the main document panel may be used to identify the cause of the leak. At some point, the number of events involved will become too large to keep straight in your head.
When an instance has too many events to easily determine which allocation or retain events do not have matching release events, you should use the Event Matching panel. Select a single instance in the document panel and then select Tools->Event Matching. This will bring up a panel like the following:
This panel splits up the events by their type and provides an easy means of matching retain events with autorelease and/or release events. Once a retain/release or retain/autorelease/release combination are selected, the group button may be clicked (or simply type 'g') to group the events. This will grey out the event records and move them to the bottom of their respective tables. Any tables with remaining elements will automatically select their next event at which point you can begin searching for the next match.
Typically the best way to use this panel is to eliminate all of the autorelease events first. Each autorelease event will coorespond with a release event stemming from NSAutoreleasePool. By examining the backtrace in the autorelease column it is usually easy to determine which alloc/retain event it should be grouped with. After all of the autorelease events have been disposed of, the remaining retain/release pairs can be matched.
At some point, you will run out of release events (since the object didn't get deallocated). The remaining alloc/retain events need to have matching release events. By examining the backtraces the proper fix is usually pretty obvious.