drawInRect retains NSImage until some point in the future?

259 views Asked by At

So I have some code running in an NSThread that creates, in a loop, a whole bunch of NSImages. A small section of each of the images is drawn into another NSImage, and then the thread exits.

So, something like

NSImage *outputImage = [[NSImage alloc] initWithSize:size];
[outputImage lockFocus];
while(1000 times)
    NSImage* image = [[NSImage alloc] initWithSize:size];
    ... image is processed ...
    [image drawInRect: ... fromRect: ... ]
[outputImage unlockFocus];

Once this is complete, the thread uses performSelectorOnMainThread to send the created NSImage back to the main thread to have it placed in a view.

This all works fine, and the final image is exactly as I expect it to be. However, during the loop the memory usage of the application rises linearly - as though each NSImage isn't released until some later time. My theory is that the drawInRect calls are being pipelined somewhere and not actually executed until later. Is this correct? And if so how do I prevent it? My application will crash if I make my loop counter too large at the moment, and I'd like to avoid that.

I've tried moving the locking and unlocking of focus into the loop, but this made no difference.

I've confirmed that if I take out only the drawInRect call, the memory usage is (more or less) flat for the lifetime of the thread. It's only when I put the call in that the memory climbs.

I'm (obviously?) using ARC in this project, and it's running in OSX10.9.

1

There are 1 answers

3
Wil Shipley On BEST ANSWER

It sounds like your NSImages are being added to the autorelease pool at some point. They won’t normally be flushed until the pool is drained.

In instances like this you want to make your own autorelease pool:

NSImage *outputImage = [[NSImage alloc] initWithSize:size];
[outputImage lockFocus];
while(1000 times) {
    @autoreleasepool {
        NSImage* image = [[NSImage alloc] initWithSize:size];
        ... image is processed ...
        [image drawInRect: ... fromRect: ... ]
    }
}
[outputImage unlockFocus];

This creates a sub-pool that will get drained on every pass, so you won’t have a huge build-up of images.