GPUImage memory usage - objective-c

I noticed that the more I use GPUImage, the more memory my app takes over time (using Instruments to monitor memory use).
As an example, I use each filter in a different methods:
(UIImage*)ToonFilter:(UIImage*)theImage
{
GPUImageSmoothToonFilter *smoothToonFilter = [[GPUImageSmoothToonFilter alloc] init];
[smoothToonFilter setTexelHeight:0.0025];
[smoothToonFilter setTexelWidth:0.0025];
return [smoothToonFilter imageByFilteringImage:theImage];
}
(UIImage*)SketchFilter:(UIImage*)theImage
{
GPUImageSketchFilter *sketchFilter = [[GPUImageSketchFilter alloc] init];
[sketchFilter setTexelHeight:0.003];
[sketchFilter setTexelWidth:0.003];
return [sketchFilter imageByFilteringImage:theImage];
}
(UIImage*)PixellateFilter:(UIImage*)theImage
{
GPUImagePixellateFilter *pixellateFilter = [[GPUImagePixellateFilter alloc] init];
[pixellateFilter setFractionalWidthOfAPixel:0.01;
return [pixellateFilter imageByFilteringImage:theImage];
}
And this is how I use these filters (testImage is a UIImage):
testImage = [self SketchFilter:testImage];
testImage = [self PixellateFilter:testImage];
If I just cycle through these filters over and over, without doing anything else, the app takes more and more memory.
What am I doing wrong? How can I release the memory once I don't need it anymore?

You keep allocating new filters every time you call these functions. Create them once in your view controller etc and send them to your functions like the sample code below (as well as a GPUImagePicture). Also don't forget to call [removeAllTargets] on your filters, sometimes they do not get removed from memory when they have targets
(UIImage*)PixellateFilter:(UIImage*)theImage withPixellateFilter:(GPUImagePixellateFilter *) pixellateFilter andStaticPicture:(GPUImagePicture *)staticPicture
and also use [filter prepareForImageCapture] before getting UIImage from your filters
For example you can build your function like
(UIImage*)PixellateFilter:(UIImage*)theImage withPixellateFilter:(GPUImagePixellateFilter *) pixellateFilter andStaticPicture:(GPUImagePicture *)staticPicture{
[staticPicture removeAllTargets];
UIImage __block *imageToReturn;
[staticPicture addTarget:pixellateFilter];
[staticPicture processImageWithCompletionHandler:^{
[pixellateFilter prepareForImageCapture];
imageToReturn = [pixellateFilter imageFromCurrentlyProcessedOutput];
[pixellateFilter removeAllTargets];
pixellateFilter = nil;
}];
return imageToReturn;
}
I use GPUImageView for my application which allows you to use GPUImageFilters and GPUImagePictures directly without getting UIImage, you should definitely consider it

Related

Is a value inside an NSDictionary released when removed?

I have something along the line of this :
#implementation ImageLoader
NSMutableDictionary *_tasks;
- (void) loadImageWithURL:(NSURL *)url callback:(SMLoaderCallback)callback {
NSMutableArray *taskList = [_tasks objectForKey:urlString];
if (taskList == nil) {
taskList = [[NSMutableArray alloc] initWithCapacity:5];
[taskList addObject:callback];
[_tasks setObject:taskList forKey:urlString];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [[UIImage alloc] initWithData:data];
for (SMLoaderCallback cb in taskList) {
cb(image, nil);
}
[_tasks removeObjectForKey:url.absoluteString];
});
});
} else {
[taskList addObject:callback];
}
}
#end
In here, I'm trying to queue up image downloads to gain performance (as an exercise only). So I'm keeping a NSDictionary that maps an URL with an array of callbacks to be called whenever the data is downloaded.
Once the image is downloaded, I no longer need this array, so I remove it from the dictionary.
I would like to know if, in this code (with ARC enabled), my array along with the callbacks are correctly released when I call [_tasks removeObjectForKey:url.absoluteString].
If your project uses ARC and that dictionary is the only thing that is referencing to those values, yes. it will be remove permanently.
ARC keeps track of number of objects that is pointing to some other object and it will remove it as soon as the count reaches 0.
so adding to dictionary -> reference count += 1
removing from dictionary -> reference count -= 1
Somewhe

Save multiple images quickly in iOS 6 (custom album)

I'm writing an application that will take several images from URL's, turn them into a UIImage and then add them to the photo library and then to the custom album. I don't believe its possible to add them to a custom album without having them in the Camera Roll, so I'm accepting it as impossible (but it would be ideal if this is possible).
My problem is that I'm using the code from this site and it does work, but once it's dealing with larger photos it returns a few as 'Write Busy'. I have successfully got them all to save if I copy the function inside its own completion code and then again inside the next one and so on until 6 (the most I saw it take was 3-4 but I don't know the size of the images and I could get some really big ones) - this has lead to the problem that they weren't all included in the custom album as they error'd at this stage too and there was no block in place to get it to repeat.
I understand that the actual image saving is moved to a background thread (although I don't specifically set this) as my code returns as all done before errors start appearing, but ideally I need to queue up images to be saved on a single background thread so they happen synchronously but do not freeze the UI.
My code looks like this:
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:singleImage]]];
[self.library saveImage:image toAlbum:#"Test Album" withCompletionBlock:^(NSError *error) {
if (error!=nil) {
NSLog(#"Error");
}
}];
I've removed the repetition of the code otherwise the code sample would be very long! It was previously where the NSLog code existed.
For my test sample I am dealing with 25 images, but this could easily be 200 or so, and could be very high resolution, so I need something that's able to reliably do this over and over again without missing several images.
thanks
Rob
I've managed to make it work by stripping out the save image code and moving it into its own function which calls itself recursively on an array on objects, if it fails it re-parses the same image back into the function until it works successfully and will display 'Done' when complete. Because I'm using the completedBlock: from the function to complete the loop, its only running one file save per run.
This is the code I used recursively:
- (void)saveImage {
if(self.thisImage)
{
[self.library saveImage:self.thisImage toAlbum:#"Test Album" withCompletionBlock:^(NSError *error) {
if (error!=nil) {
[self saveImage];
}
else
{
[self.imageData removeObject:self.singleImageData];
NSLog(#"Success!");
self.singleImageData = [self.imageData lastObject];
self.thisImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:self.singleImageData]]];
[self saveImage];
}
}];
}
else
{
self.singleImageData = nil;
self.thisImage = nil;
self.imageData = nil;
self.images = nil;
NSLog(#"Done!");
}
}
To set this up, I originally used an array of UIImages's but this used a lot of memory and was very slow (I was testing up to 400 photos). I found a much better way to do it was to store an NSMutableArray of URL's as NSString's and then perform the NSData GET within the function.
The following code is what sets up the NSMutableArray with data and then calls the function. It also sets the first UIImage into memory and stores it under self.thisImage:
NSEnumerator *e = [allDataArray objectEnumerator];
NSDictionary *object;
while (object = [e nextObject]) {
NSArray *imagesArray = [object objectForKey:#"images"];
NSString *singleImage = [[imagesArray objectAtIndex:0] objectForKey:#"source"];
[self.imageData addObject:singleImage];
}
self.singleImageData = [self.imageData lastObject];
self.thisImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:self.singleImageData]]];
[self saveImage];
This means the rest of the getters for UIImage can be contained in the function and the single instance of UIImage can be monitored. I also log the raw URL into self.singleImageData so that I can remove the correct elements from the array to stop duplication.
These are the variables I used:
self.images = [[NSMutableArray alloc] init];
self.thisImage = [[UIImage alloc] init];
self.imageData = [[NSMutableArray alloc] init];
self.singleImageData = [[NSString alloc] init];
This answer should work for anyone using http://www.touch-code-magazine.com/ios5-saving-photos-in-custom-photo-album-category-for-download/ for iOS 6 (tested on iOS 6.1) and should result in all pictures being saved correctly and without errors.
If saveImage:toAlbum:withCompletionBlock it's using dispatch_async i fear that for i/o operations too many threads are spawned: each write task you trigger is blocked by the previous one (bacause is still doing I/O on the same queue), so gcd will create a new thread (usually dispatch_async on the global_queue is optimized by gcd by using an optimized number of threads).
You should either use semaphores to limit the write operation to a fixed number at the same time or use dispatch_io_ functions that are available from iOS 5 if i'm not mistaken.
There are plenty example on how to do this with both methods.
some on the fly code for giving an idea:
dispatch_semaphore_t aSemaphore = dispatch_semaphore_create(4);
dispatch_queue_t ioQueue = dispatch_queue_create("com.customqueue", NULL);
// dispatch the following block to the ioQueue
// ( for loop with all images )
dispatch_semaphore_wait(aSemaphore , DISPATCH_TIME_FOREVER);
[self.library saveImage:image
toAlbum:#"Test Album"
withCompletionBlock:^(NSError *error){
dispatch_semaphore_signal(aSemaphore);
}];
so every time you will have maximum 4 saveImage:toAlbum, as soon as one completes another one will start.
you have to create a custom queue, like above (the ioQueue) where to dispatch the code that does the for loop on the images, so when the semaphore is waiting the main thread is not blocked.

Analyzer claiming an object was released when it wasn't

I am getting a static analysis error in this code which doesn't make any sense to me. The error is:
Reference-counted object is used after it is released
This is glue code to allow for PNG loading in a game originally written in C++.
int pngLoad(const char *filename, pngInfo *info, int format, GLuint *textureName)
{
char fullPath[Engine::Settings::MaxPath];
strcpy(fullPath, filename);
appendAppBundlePath(fullPath);
NSString *path = [NSString stringWithCString:fullPath encoding:NSUTF8StringEncoding];
NSData *data = [[NSData alloc] initWithContentsOfFile:path];
UIImage *image = [[UIImage alloc] initWithData:data];
[data release];
Texture2D *loadedTex = [Texture2D alloc];
// ##### Analyzer claims the object is released here: #####
[loadedTex initWithImage:image format:format];
int didLoad;
// ##### Error is here: #####
if (loadedTex.contentSize.width == 0 || loadedTex.contentSize.height == 0)
{
didLoad = 0;
}
else
{
didLoad = 1;
*textureName = loadedTex.name;
// return texture info
info->ScaleFactor = loadedTex.scaleFactor;
info->Width = (float)image.size.width / (float)info->ScaleFactor;
info->Height = (float)image.size.height / (float)info->ScaleFactor;
info->Alpha = 1;
info->PaddedWidth = loadedTex.pixelsWide;
info->PaddedHeight = loadedTex.pixelsHigh;
}
[loadedTex release];
[image release];
return didLoad;
}
If I use Texture2D *loadedTex = [[Texture2D alloc] retain]; this warning is removed, but then an warning that I've leaked an object comes up, so something is seriously weird here.
initWithImage:format: used to contain a [self release] which shouldn't have been there, which I removed when I found this warning. However, even after a full clean and rebuild, I still get the warning. Am I doing something else wrong? Is something not getting properly cleaned up by the Clean command in Xcode?
The analyzer may be right, at least in a general way.
Texture2D *loadedTex = [Texture2D alloc];
[loadedTex initWithImage:image format:format];
In general, "init" might actually discard the object passed in and return a different one. Whether or not this is the case for "Texture2D" is something I don't know, but if the analyzer is going for the general case then it is right.
You should be able to work around that by using
Texture2D *loadedTex = [Texture2D alloc];
loadedTex=[loadedTex initWithImage:image format:format];
Or by simply combining the two calls, as it is done in most Objective-C examples.
You should always combine the alloc and initXXX call when creating objects, in this case
Texture2D *loadedTex = [[Texture2D alloc] initWithImage:image format:format];
An init method need not return the same object it was called with, it is allowed to return a different object.
In this case your result from loadedTex = [Texture2D alloc] would be released and initWithImage would return a different object (which you discard).

OS X ARC memory increase in repeated image loading

I have come across an issue when loading different NSImages repeatedly in an application written using Automatic Reference Counting. It seems as though ARC is not releasing the image objects correctly, and instead the memory usage increases as the list is iterated until the iteration is complete, at which point the memory is freed.
I am seeing up to 2GB of memory being used through the process in some cases. There's a good discussion on SO on a very similar issue which ends up putting the process in an NSAutoReleasePool and releasing the image and draining the pool. This works for me as well if I don't use ARC, but it is not possible to make calls to these objects / methods in ARC.
Is there any way to make this work in ARC as well? It seems as though ARC should be figuring this out all by itself - which makes me think that the fact that these objects are not getting released must be a bug is OS X. (I'm running Lion with XCode 4.2.1).
The kind of code which is causing the issue looks like this:
+(BOOL)checkImage:(NSURL *)imageURL
{
NSImage *img = [[NSImage alloc] initWithContentsOfURL:imageURL];
if (!img)
return NO;
// Do some processing
return YES;
}
This method is called repeatedly in a loop (for example, 300 times). Profiling the app, the memory usage continues to increase with 7.5MB alloced for each image. If ARC is not used, the following can be done (as suggested in this topic):
+(BOOL)checkImage:(NSURL *)imageURL
{
NSAutoReleasePool *apool = [[NSAutoReleasePool alloc] init];
NSImage *img = [[NSImage alloc] initWithContentsOfURL:imageURL];
if (!img)
return NO;
// Do some processing
[img release];
[apool drain];
return YES;
}
Does anyone know how to force ARC to do the memory cleaning? For the time being, I have put the function in a file which is compiled with -fno-objc-arc passed in as a compiler flag. This works OK, but it would be nice to have ARC do it for me.
use #autoreleasepool, like so:
+(BOOL)checkImage:(NSURL *)imageURL
{
#autoreleasepool { // << push a new pool on the autotrelease pool stack
NSImage *img = [[NSImage alloc] initWithContentsOfURL:imageURL];
if (!img) {
return NO;
}
// Do some processing
} // << pushed pool popped at scope exit
return YES;
}

Syntax/formatting when nesting objective-c blocks

I'm nesting blocks, and it looks UGGGGLY. Is there a way to write this less ugly? Mostly looking for syntax suggestions, rather than structural, but I'll accept either.
My block factory method,
-(NSImage *(^)(CGFloat size, BOOL preview))resizeBlock {
return (NSImage *(^)(CGFloat size, BOOL preview))[[^(CGFloat size, BOOL preview){
// image-resizing code
return [[[NSImage alloc] init] autorelease];
} copy] autorelease];
}
Which is called from a number of functions similar to this,
-(void)queueResize:(CGFloat)targetSize toView:(NSImageView *)targetView {
NSImage*(^sizeBlock)(CGFloat,BOOL) = [self resizeBlock];
NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^(void) {
NSImage *previewImage = (NSImage*)sizeBlock(targetSize,YES);
targetView.image = previewImage;
}];
[queue addOperation:bo];
}
queue is an NSOperationQueue object. It won't compile without all the (ugly ugly) casting. Amidoinitrite?
Edit:
As per Dave DeLong's answer, and http://www.cimgf.com/2008/02/16/cocoa-tutorial-nsoperation-and-nsoperationqueue/, I changed the line
targetView.image = previewImage;
to be,
[targetView performSelectorOnMainThread:#selector(setImage:) withObject:previewImage waitUntilDone:YES];
Use typedef:
typedef NSImage *(^KWResizerBlock)(CGFloat size, BOOL preview);
This makes your code become:
- (KWResizerBlock) resizeBlock {
KWResizerBlock block = ^(CGFloat size, BOOL preview){
// image-resizing code
return [[[NSImage alloc] init] autorelease];
};
return [[block copy] autorelease];
}
-(void)queueResize:(CGFloat)targetSize toView:(NSImageView *)targetView {
KWResizerBlock sizeBlock = [self resizeBlock];
NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
NSImage *previewImage = sizeBlock(targetSize, YES);
//do something with previewImage
}];
[queue addOperation:bo];
}
One word of caution:
Your NSBlockOperation is going to be executing on a thread that's not the main thread, and so it is unsafe to manipulate any UI element from within that context. If you need to put the previewImage onto the UI, then you should dispatch_async() back to the main thread (or something functionally equivalent).
It may work right now, but it is strongly discourage and can lead to undefined behavior.