Question

I'm getting a memory error. I'm getting a memory error because the memory usage increases exponentially. Clearly I am not releasing something, any ideas what that may be?

Here is my method to determine red pixels present in a UI image. It returns the count.

- (NSUInteger)getRedPixelCount:(UIImage*)image
{
NSUInteger numberOfRedPixels = 0;

struct pixel* pixels = (struct pixel*) calloc(1, image.size.width * image.size.height * sizeof(struct pixel));
if (pixels != nil)
{
    CGContextRef context = CGBitmapContextCreate((void *) pixels,
                                                 image.size.width,
                                                 image.size.height,
                                                 8,
                                                 image.size.width * 4,
                                                 CGImageGetColorSpace(image.CGImage),
                                                 (CGBitmapInfo)kCGImageAlphaPremultipliedLast);

    if (context != NULL)
    {
        CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, image.size.width, image.size.height), image.CGImage);
        NSUInteger numberOfPixels = image.size.width * image.size.height;

        while (numberOfPixels > 0) {
            if (pixels->r == 255) {
                numberOfRedPixels++;

            } 
            pixels++;
            numberOfPixels--;
        }
        CGContextRelease(context);
    }
}
return numberOfRedPixels;

}

This is the code to iterate through the photo library images and determine each of their red pixels.

[self.library enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
    if (group) {
        [group setAssetsFilter:[ALAssetsFilter allPhotos]];
        [group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop){

            if (asset) {
                ALAssetRepresentation *rep = [asset defaultRepresentation];
                CGImageRef iref = [rep fullResolutionImage];

                if (iref){

                        UIImage *myImage = [UIImage imageWithCGImage:iref scale:[rep scale] orientation:(UIImageOrientation)[rep orientation]];

                        NSLog(@"%i", [self getRedPixelCount:myImage]);
                }
            }
        }];
    }
} failureBlock:^(NSError *error) {
    NSLog(@"error enumerating AssetLibrary groups %@\n", error);
}];

Regards, C.

Was it helpful?

Solution

You're not releasing the memory allocated by

struct pixel* pixels = (struct pixel*) calloc(1, image.size.width * image.size.height * sizeof(struct pixel));

You need to add:

free(pixels);

at the bottom of the if(pixels != nil) block.

Make the first block look like:

- (NSUInteger)getRedPixelCount:(UIImage*)image
{
    NSUInteger numberOfRedPixels = 0;

    struct pixel* pixels = (struct pixel*) calloc(1, image.size.width * image.size.height * sizeof(struct pixel));
    if (pixels != nil)
    {
        CGContextRef context = CGBitmapContextCreate((void *) pixels,
                                                     image.size.width,
                                                     image.size.height,
                                                     8,
                                                     image.size.width * 4,
                                                     CGImageGetColorSpace(image.CGImage),
                                                     (CGBitmapInfo)kCGImageAlphaPremultipliedLast);

        if (context != NULL)
        {
            CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, image.size.width, image.size.height), image.CGImage);
            NSUInteger numberOfPixels = image.size.width * image.size.height;
            struct pixels* ptr = pixels;

            while (numberOfPixels > 0) {
                if (ptr->r == 255) {
                    numberOfRedPixels++;

                } 
                ptr++;
                numberOfPixels--;
            }
            CGContextRelease(context);
        }
        free(pixels);
    }
    return numberOfRedPixels;
}

Also it will help if the second block changes to include:

        @autoreleasepool {
            ALAssetRepresentation *rep = [asset defaultRepresentation];
            CGImageRef iref = [rep fullResolutionImage];

            if (iref){

                UIImage *myImage = [UIImage imageWithCGImage:iref scale:[rep scale] orientation:(UIImageOrientation)[rep orientation]];

                NSLog(@"%i", [self getRedPixelCount:myImage]);
                <#statements#>
            }
        }

although the major leak is not freeing the pixel buffer.

OTHER TIPS

You can embrace 'enumerateAssetsUsingBlock' block with @autorelease:

@autorelease {

   if (asset) {

      ...
   }
}

This force all autoreleasing objects to release immediately.

UPD: Use [asset thumbnail] instead of fullScreenImage or fullResolutionImage. These methods generate huge amount of pixel data.

UPD2: if even [asset thumbnail] did not help, than you must find a way to release this image data, it could be little bit tricky as long as you can't to release it directly calling CGImageRelease. Try something like this:

NSMutabelArray* arr = [NSMutabelArray new];

In your enumerateAssetsUsingBlock just put asset object in this array:

[arr addObject:asset];

And do nothing else in this block.

Then iterate through this array in a way:

while (arr.count > 0)
{
   ALAsset* asset = [arr lastObject];
   // do smth with asset
   [arr removeLastObject]; // this will remove object from memory immediatelly
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top