Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

PortlandMac

macrumors newbie
Original poster
Feb 27, 2010
4
0
I'm programming in objective-c and cocoa. The application does image processing and makes extensive use of OS utilities. When batch processing my application goes into a loop to process one image on each pass through the loop. On completion the program's dialog box is still visible so the user can process more images.

Under 10.5 resources are allocated and released for each pass in the loop.

Under 10.6 resources are allocated but the release does not occur until the function returns to the OS when all resources are released.

I've verified this is the case by placing a breakpoint at every function in the program and the Activity Monitor doesn't back off until this function returns to the OS.

When users batch process many large images the memory requirements can easily go into the gigabytes. My thinking at this time is to place the body of the loop into a function and send a message, via NSNotification, to trigger the function for each image. This is in hopes that the message will get queued along with resource management so that resources are properly released for each image.

Any thoughts?
 
Let me see if I can summ up what you are saying: You have a Cocoa (or at least Foundation program using NSRunLoop) that can batch-process images and have a loop within your code that serially grabs images from disk and processes them. While you are in this loop you are noticing that even though you are done with an image (presumably you have "release"ed this image) you are not see heap memory usage decrease until you have passed out of your loop, and exited your function (so implicitly returned control to NSRunLoop).

If that is what you are saying, then this is expected behavior, since it is one of NSRunLoop's jobs to trigger memory cleanups. You have already touched on one way to go (make sure that you are dropping back into NSRunLoop more often).

The second way of doing this it to be more explicit about your memory control, by using NSAutoreleasePool directly (rather than indirectly like you are now):
Code:
- (void) processImages:(NSArray *)sourceArray {
	// making the huge assumption that the items in sourceArray are NSImages
	
	int imagesInMemory = 0
	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	for (NSImage * in sourceArray) {
		// nifty changes here
		
		if (imagesInMemory > 5) {
			[pool drain];
			imagesInMemory = 0;
		} else {
			imagesInMemory++;
		}
	}
	[pool release];
}
Note that that is meta-code, and has not been tested. It has also been a little while since I last needed to play with NSAutoreleasePool, so there might be something I screwed up there. But it should lead you in the right direction.

The third way is to use GrandCentral and blocks. From what you are describing you are probably a perfect candidate for this and it would simplify and speed up your code, and your memory issues would also get swept away in the process. But this is going to make it 10.6-only, and require you to learn a new way of programming (though not that hard).
 
Tried the pool approach but didn't help. Suspect it's because I have stuff in the code like malloc/free. I suppose I could "new" a character array.

Not using garbage collection.
 
I implemented the NSNotification scenario but still have the same issue.

Aside from opening large images this application also reads a serialized database that gets transformed into threaded lists. If I have to switch from malloc/free to NS calls there will be a rather large rewrite.

Sigh...
 
Is there a practical way that you could just reuse the same memory blocks and realloc() them instead of disposing of them?
 
Problem solved

I placed the body of the loop in a separate function and then did a NSTimer instead of NSNotification. This works even with the timer set at 0 seconds which resolves to 0.1ms delay.

Just had to get into the right OS queue!
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.