iOS Programming memory issue with ALAssets enumeration with ARC - memory

I wish to enumerate over my album, but when i test the app on my device, the app crashes after 30 seconds. when I used Instrument, i discovered that ARC doesn't autorelease the assets, so every time it deals with an image, it allocates the image size. My main goal is showing only device's screenshots. so lets say i have 1000 photos, after 30 images i get 30MB allocation, which causes the crash. How do I explicitly release the memory?
Here is my code:
[self.assetsGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result) {
ALAssetRepresentation* representation = [result defaultRepresentation];
// Retrieve the image orientation from the ALAsset
UIImageOrientation orientation = UIImageOrientationUp;
NSNumber* orientationValue = [result valueForProperty:#"ALAssetPropertyOrientation"];
if (orientationValue != nil) {
orientation = [orientationValue intValue];
}
CGFloat scale = 1;
UIImage* image = [UIImage imageWithCGImage:[representation fullResolutionImage]
scale:scale orientation:orientation];
if ( image.size.height/2==screenHeight && image.size.width/2==screenWidth)
[self.assets addObject:result];
}
}];
I think default representation causes the malloc.
Thanks in advance...

You've discovered that UIImage objects can take up a lot of memory when they're created.
Instead of creating UIImage objects, why not use API's that ALAsset provides to you that don't hog memory. For example, you can get the image dimensions (I've linked the Apple documentation for you).
Also, this line:
if ( image.size.height/2==screenHeight && image.size.width/2==screenWidth)
seems a little dangerous to me. Unless you control all the images that are being loaded, any full resolution image is very likely to be a different size than exactly 1/2 of the screen height and width.

Related

get all the images from camera roll and show it on the scroll view?

Currently i am using ALasset library for fetching the images but when the images in the camera roll is in high amount i am getting the memory warning issues.
e.g assetsd died
this is my code to get the images from camera roll
[self.assetsGroup enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:(NSMakeRange(0, self.assetsGroup.numberOfAssets - 1))] options:NSEnumerationReverse usingBlock:^(ALAsset result, NSUInteger index, BOOL stop)
{
if (result)
{
UIImage *imageThumbnail;
imageThumbnail = [ImageHelper scaleAndRotateImage:[UIImage imageWithCGImage:[[result defaultRepresentation] fullScreenImage]] maxSize:500];
if(imageThumbnail)
{
//Do your code here….
}
}];
How i can resolve the memory warning and asset died problem.
Also Can i known the apps which are having scroll view containing all the camera roll images but in scroll display not in grid form? So that i can get the reference of it.
Thanks in advance.
I think instead of scroll view you have to use collection view with row = self.assetsGroup.numberOfAssets - 1 and in collection view cell write code as follow
[self.assetsGroup enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:(NSMakeRange(0, indexPath.row))] options:NSEnumerationReverse usingBlock:^(ALAsset result, NSUInteger index, BOOL stop)
{
if (result)
{
UIImage *imageThumbnail;
imageThumbnail = [ImageHelper scaleAndRotateImage:[UIImage imageWithCGImage:[[result defaultRepresentation] fullScreenImage]] maxSize:500];
if(imageThumbnail)
{
//Do your code here….
}
}];
And one-thing more imageThumbnail size is in few kb so you can store it ( in local directory or in coredata usging nsdata )and before use enumerateAssetsAtIndexes check image is already stored then do not call use that stored image

UIImageView stops displaying images after a specific amount of loop iterations

My iOS app utilizes a loop to cycle through images in a folder.
My application is supposed to loop through a total of 2031 images (sized 1200x900) inside a folder. The images were taken at 8fps and each image will be displayed as the loop continues to simulate a video clip. After the 696th picture, the images will cease to be displayed in the UIImageView although the app will continue looping.
I tested to see if the disconnect was because of the picture not existing
I started the loop at picture 200, but after picture 896 the UIImageView stop displaying the pictures.
The Code:
imgName = [NSString stringWithFormat:#"subject_basline_mat k (%d).png",jojo];
jojo++;
imageToCrop.image = [UIImage imageNamed:imgName]; //imageToCrop is the name of the UIImageView image and it is set to the image file here
imageToCrop.image = [self imageWithImage:imageToCrop.image convertToSize:self.imageToCrop.frame.size]; //Here the image is converted to fit the bounds of the simulator which is 320x240
The code loops due to a timer that loops it about once every 0.8 seconds.
I ran my code with instruments to see if there was a memory problem occurring,and instruments is very heavy on my computer. As such, my application ran quite slowly. However, when I arrived at the 696th picture, the pictures kept displaying themselves. It was almost as if my application running too quickly caused the picture to not be displayed... which I don't really understand.
The only memory heavy part of the image switching seems to be the size conversion step which is called by the line imageToCrop.image = [self imageWithImage:imageToCrop.image convertToSize:self.imageToCrop.frame.size];
imageToCrop.image = [self imageWithImage:imageToCrop.image convertToSize:self.imageToCrop.frame.size];
The method "imageWithImage" is here:
- (UIImage *)imageWithImage:(UIImage *)image convertToSize:(CGSize)size {
#autoreleasepool {
UIGraphicsBeginImageContext(size);
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage *destImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return destImage;
}
And the line [image drawInRect:CGRectMake(0, 0, size.width, size.height)]; uses around up the most memory out of all the image management in the app.
Any Ideas as to why my app will only display a certain amount of images?
Try loading the full-size images from the app bundle by URL. For example:
#autoreleasepool {
NSString *imgName = [NSString stringWithFormat:#"subject_basline_mat k (%d)",jojo];
NSURL *imageURL = [[NSBundle mainBundle] URLForResource:imgName withExtension:#"png"];
UIImage *image = [UIImage imageWithContentsOfFile:[imageURL path]];
imageToCrop.image = [self imageWithImage:image convertToSize:self.imageToCrop.frame.size];
}
Almost for sure your problem is [UIImage imageNamed:imgName]. There are hundreds of posts here on the pitfalls of using it. The issue is that it caches the images - its real purpose is for some small number of images in your bundle.
If you have oodles of images, get the path to the image, then get the image through a URL or file pointer. That way its not cached. Note that when you do this, you lose the automatic "get-retina-image-automatically", and so you will need to grab the appropriately sized image depending on whether the device is retina or not.

Received memory warning when capturing screen and save to video ios

I am now writing a program to capture screen and convert to video. I can successfully save the video if it is less than 10 seconds. But, if more than that, I received memory warning and application crash. I wrote this code as follow. Where am I missing to release data ? I would like to know how to do.
-(void)captureAndSaveImage
{
if(!stopCapturing){
if (assetWriterInput.readyForMoreMediaData)
{
keepTrackOfBackGroundMood++;
NSLog(#"keepTrackOfBackGroundMood is %d",keepTrackOfBackGroundMood);
CVReturn cvErr = kCVReturnSuccess;
CGSize imageSize = screenCaptureAndDraw.bounds.size;
CGFloat imageScale = 0; //if zero, it reduce processing time
if (NULL != UIGraphicsBeginImageContextWithOptions)
{
UIGraphicsBeginImageContextWithOptions(imageSize, NO, imageScale);
}
else
{
UIGraphicsBeginImageContext(imageSize);
}
[self.hiddenView.layer renderInContext:UIGraphicsGetCurrentContext()];
[self.screenCaptureAndDraw.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
image = (CGImageRef) [img CGImage];
CVPixelBufferRef pixelBuffer = NULL;
CFDataRef imageData= CGDataProviderCopyData(CGImageGetDataProvider(image));
cvErr = CVPixelBufferCreateWithBytes(kCFAllocatorDefault,
FRAME_WIDTH2,
FRAME_HEIGHT2,
kCVPixelFormatType_32BGRA,
(void*)CFDataGetBytePtr(imageData),
CGImageGetBytesPerRow(image),
NULL,
NULL,
NULL,
&pixelBuffer);
//CFRelease(imageData);
//CGImageRelease(image); //I can't write this code because I am not creating it and when I check online, it say it is not my responsibility to release. If I write, the application crash immediately
// calculate the time
CFAbsoluteTime thisFrameWallClockTime = CFAbsoluteTimeGetCurrent();
CFTimeInterval elapsedTime = thisFrameWallClockTime - firstFrameWallClockTime;
// write the sample
BOOL appended = [assetWriterPixelBufferAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:presentationTime];
if (appended) {
NSLog (#"appended sample at time %lf and keepTrackofappended is %d", CMTimeGetSeconds(presentationTime),keepTrackofappended);
keepTrackofappended++;
} else {
NSLog (#"failed to append");
[self stopRecording];
//self.startStopButton.selected = NO;
screenRecord=false;
}
}
}//stop capturing
// });
}
I agree that you don't want to do the CGImageRelease(image). This object was created by calling CGImage method of a UIImage object. Thus ownership was not transferred and ARC still does the memory management for your img object and no releasing of the image object is needed.
But I think you do want to restore your CFRelease(imageData). This is an object created by CGDataProviderCopyData, so you own it and must clean up.
I also think you have to release the pixelBuffer that you created with CVPixelBufferCreateWithBytes after you appendPixelBuffer. You can use the CVPixelBufferRelease function for that.
The Core Foundation memory rule is that if the function has Copy or Create in the name, you own that object and are responsible for releasing it. See the Create Rule in the Memory Management Programming Guide for Core Foundation.
I would have thought that the static analyzer (shift+command+B or "Analyze" from the Xcode "Product" menu) would have identified this issue, as it has gotten much better at finding Core Foundation memory issues (albeit, not perfect).
Alternatively, if you run your app through the Leaks tool in Instruments (which will also show you the Allocations tool at the same time), you can take a look at your memory usage. While the video capture requires a lot of Live Bytes, in my experience it stays pretty darn flat. If it's growing, you have a leak somewhere.

Apparent leaks: png_malloc

I have an application with various animations and images. The application runs just fine for about 30 minutes, but then crashes. I have looked through the instruments and I notice that there are a whole bunch of 7kB png_malloc allocations building each time I mark the heap (amounting to about 300kB every couple minutes).
I noticed in my leaks that every time an animation or png is used for the first time, there seems to be a "leak" of the data (although I am a bit skeptical whether this is a real leak or not).
All of these images have been declared using
frameName = [[NSString alloc] initWithFormat:#"image.png"];
UIImage * u = [UIImage cachelessImageNamed:frameName];
so I don't believe there should be a problem with caching the images.
Has anyone else had the same problem with this png_malloc allocation?
The instruments screenshot
*Notes: I am using arc and the animations are getting set to nil in the deallocation function; however, these isn't called until the application exits. Does this create a problem each time the animation is run if it's only been created once?
EDIT Some more code:
-(void) createSymbolAnimations
{
if (symbolAnimations == nil)
{
symbolAnimations = [[NSMutableArray alloc]init];
}
NSString * frameName;
if (thisAnimation == nil)
{
thisAnimation = [[NSMutableArray alloc] init];
}
for (int x= 0; x< 40; x++)
{
frameName = [[NSString alloc] initWithFormat:#"image%d%s",x,".png"];
UIImage * u = [UIImage cachelessImageNamed:frameName];
[thisAnimation addObject:u];
}
[symbolAnimations addObject:thisAnimation];
}
Is the creation of the animation. Imagine I have a few of these and then I change the animation set and start animating on touch with this snippet:
UIImageView * aView = [frameArray objectAtIndex:x];
aView.image = [[symbolAnimations objectAtIndex:x]objectAtIndex:0];
[aView startAnimating];
Where x is the set of images I want to animate and 0 is the first frame of the animation.
So the image is changed quite a few times and I'm starting to worry that each time the animation images are changed, the RAM isn't cleared but instead over/rewritten.
EDIT Image grabber
+(UIImage *) cachelessImageNamed: (NSString *) name
{
return [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:name ofType:nil]];
}
Just in case anyone stumbles upon this later, I found the problem.
The pngs used in this project for animations were created in Windows (not sure how pertinent that is) and it seems the file format is slightly different than the png that XCode is expecting. This disallows any png from being deallocated. If you convert the format to a png for Mac, it seems to work fine. I did this through
mogrify -type truecolormatte -format png *.png
After adjusting all of my images, the leaks were greatly reduced and everything seems to run fine.

Memory leak with a lot of big images in iPad

I'm trying to store UIImage datas in NSArray, actually 60 images, each with size of 300kb. Then, I'm trying to animate that images in UIImageView.
My code:
NSMutableArray *arr = [[NSMutableArray alloc] init];
for (int i=0; i<61; ++i) {
[arr addObject:[UIImage imageNamed:[NSString stringWithFormat:#"%i.png", i]]];
}
img.animationImages = arr;
img.animationDuration = 1.5;
img.contentMode = UIViewContentModeBottomLeft;
[img startAnimating];
When I test it in iPad Simulator 4.3, it works fine.
When I want to test it on my device (iPad 1), application crashes.
Note: App does not crashes if I comment this code: [img startAnimating];
1. What could be the problem? I think it is memory problem...?!
2. Can I store a loooots of UIImages in NSArray?
Your png files may have 300KB, but this is a compressing format.
To get an idea of the size of the Image itself you should multiply the width, height and bytes per pixel.
i.e. If an image has the size 1024 * 1024 and RGBA model, the images itself has an size of 4MB in the memory. And this is just one image. If you have 300 it is about 120MB.
Note: this is only a rough rule of thumb, but it gives you an idea.
So you should keep path-names in the array and load the image only if need, and thumbnails should be resized and stored on the disk as files. Do not just scale the UIImageView.
Here's a great article with code about resizing.
I solved the app crash problem with alternative way: I tried to use simple NSTimer. For each iteration I set n'th image to UIImageView:
I have UIImageView in xib file, pointing to img variable.
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:.03
target:self
selector:#selector(tiktak)
userInfo:nil repeats:YES];
int n = 0;
-(void)tiktak {
n++;
img.image = [UIImage imageNamed:
[NSString stringWithFormat:#"Blue Menu_%i.png", n]];
if(nn == 60) nn = 0;
}
Animation is amazing. I've also tested it in Activity Monitor, CPU usage is 6% and less.
I would not advise you store that many UIImages in an NSArray, its way too heavy and inefficient. From where are these images from (bundle, internet, device's gallery)?
Edit: I don't know how you want it to look, but you can build the array with the names of the UIImages, and then load the UIImage and show it. Like this:
for(i=0;i<60;i++){
NSString *nameOfPicture=[NSString stringWithFormat:#"picture_%d",i+1]
[array addObject:nameOfPicture];
}

Resources