R – iPhone + UIView. Enormous memory consumption during drawRect. Any strategies for reducing this

drawrectiphoneuiview

My data visualization app incurs a large memory consumption spike during redraw (setNeedsDisplay which triggers drawRect). I am currently redrawing the entire view that houses the data plot. This view is much larger then the device display.

Is there any way to tell CoreGraphics to allocate just enough memory to draw each element (each element is a small rectangular block much smaller then the device display) and release the memory when done, rather then my current naive approach?

Thanks in advance.

-Doug

UPDATE 8 Dec 8:28am EST

Here is the relevant code with explanatory wordage. I am running Instruments with ObjectAlloc, Memory Monitor, and Leaks instruments running. The only memory leak I have is due has to do with the NSOperationQueue not releasing mems. This is minor an not relevant.

Architecturally the app consists of a tableView with a list of interesting locations along the human genome to inspect. When a table row is selected I enqueue a data gathering operation that returns data called alignmentData. This data is then plotted as horizontal rectangular slabs.

Initially, when the tableView launches my memory footprint is 5 MB.

- (void)viewWillAppear:(BOOL)animated {

    // Initial dimensions for the alignment view are set here. These
    // dimensions were roughed out in IB.

    frame                       = self.alignmentView.frame;
    frame.origin.x              = 0.0;
    frame.origin.y              = 0.0;  
    frame.size.width            = self.scrollView.contentSize.width;    
    frame.size.height           = 2.0 * (self.containerView.frame.size.height);

}

Note: After viewWillAppear: is called the memory footprint has not budged. Even though the alignmentView is be sized well beyond the dimensions of the display.

This is the method called from the data gathering operation.

- (void)didFinishRetrievingAlignmentData:(NSDictionary *)results {

        // Data retrieved from the data server via the data gathering operation
    NSMutableData *alignmentData = [[results objectForKey:@"alignmentData"] retain];

    NSMutableArray *alignments = [[NSMutableArray alloc] init];
    while (offset < [alignmentData length]) {

        // ...
        // Ingest alignmentData in alignments array 
        // ...  

    } // while (offset < [alignmentData length])    
    [alignmentData release];

    // Take the array of alignment objects and position them in screen space 
        // so that they pack densely creating horizontal rows of alignment objects 
        // in the process.
    self.alignmentView.packedAlignmentRows = 
    [Alignment packAlignments:alignments basepairStart:self.startBasepairValue basepairEnd:self.endBasepairValue];  
    [alignments release];

    [self.alignmentView setNeedsDisplay];

}

After this line of code:

self.alignmentView.packedAlignmentRows = ...

The memory footprint is 13.8 MB

After this line of code:

[self.alignmentView setNeedsDisplay];

The memory footprint spikes to 21.5 MB, stays there for a few seconds then returns to the pre-existing level of 13.8 MB

The solution I am looking for would allow me to essentially, create a horizontal render buffer window that that is the height of a single row of alignment objects. I would allocate its memory render into it, then discard it. I would do this over and over again for each row of alignment data.

In theory, I could render an infinite amount of data with this approach which of course would be most excellent ;-).

-Doug

Best Answer

Here is the - not so obvious answer to my memory problem. I'll give myself this one because I learned it on the Apple dev forum form Rincewind - a very helpful Apple engineer BTW.

It turns out that by slicing a large view into N smaller pieces and rendering into each in turn I will incur a memory spike that is roughly 1/N the size of the large view.

So, for each smaller view: alloc/init, feed a portion of my data, setNeedsDisplay. Rinse/repeat for all N small views.

Simple, eh?

Prior to learning this I had mistakenly thought that setNeedsDisplay:myRect did this for the large view. Apparently not.

Thanks for all the suggestions gang.

Cheers, Doug @dugla

Related Topic