I am using 150 images in a sequence for animation .
Here is my code.
NSMutableArray *arrImages =[[NSMutableArray alloc]initWithCapacity:0];
for(int i = 0; i <=158; i++)
{
UIImage* image = [UIImage imageNamed:[NSString stringWithFormat:#"baby%05d.jpg",i]];
[arrImages addObject:image];
}
babyimage.animationImages = arrImages;
[arrImages release];
babyimage.animationDuration=6.15;
[babyimage startAnimating];
but it is taking too much memory.After playing it for 1 minute it shows memory warnings in console. and then crashed.i have reduced images resolution also and i can't make it less then 150 for better quality.
Is there any better way to do this animation without memory issue.
Thanks a lot
plz help ...
Instead of
[UIImage imageNamed:[NSString stringWithFormat:#"baby%05d.jpg",i]]
use
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"baby%05d.jpg",i] ofType:nil]]
Reason is imageNames caches image and does not release until it release memory warning
Edit
Also, don't store entire image into array, just save image name if you want or don't save anything. This will also take much memory.
I am unaware as to what the animation is you haven't specified it. But if I were doing the code, I would follow the following algorithm.
This is assuming there's only one image shown at a particular time but during transition, you will end up having two convolving in a way.
procedureAnimate:
photo_1 = allocate(file_address_array[i++])
while i < MAX_PHOTOS
photo_2 = allocate(file_address_array[i++])
photo_3 = allocate(file_address_array[i++])
perform_animation(photo_1, photo_2)
release(photo_1)
photo_1 = photo_2
photo_2 = photo_3
release(last_two)
I not sure if this is the_perfect_way of doing such a thing. But this will be a lot more efficient.
(May be there's something terribly wrong with the alloc/release login but this is consistent with my working at 5.14 AM in the morning). Let me know if this doesn't work.
Related
I'm using the profiler in xcode 4 to determinate if I have any memory leaks. I didn't have this leak before, but with xcode 5 I have this one.
I'm trying to set an image for the tab item of my `UIViewController and the profiler marks this line :
image = [[UIImage alloc] initWithContentsOfFile:imgPath]; <<=== Leak : 9.1%
This is part of my code I don't understand why. What's the best way to resolve this issue?
NSString *imgPath;
UIImage *image;
IBNewsViewController *newsView = [[IBNewsViewController alloc] initWithURL:[tvLocal urlFlux] title:#"News" isEmission:NO];
[newsView setTitle:#"News"];
imgPath = [[NSBundle mainBundle] pathForResource:"news" ofType:#"png"];
image = [[UIImage alloc] initWithContentsOfFile:imgPath]; <<=== Leak : 9.1%
newsView.tabBarItem.image = image;
[image release];
image = nil;
UINavigationController* navNew = [[UINavigationController alloc] initWithRootViewController:newsView];
[newsView release];
newsView = nil;
EDIT:
No leak on iOS6.
Why it's leak on iOS7?
You should switch to the autoreleasing imageNamed: method. This has the added benefit of system level cacheing of the image.
NSString *imgPath;
UIImage *image;
IBNewsViewController *newsView = [[IBNewsViewController alloc] initWithURL:[tvLocal urlFlux] title:#"News" isEmission:NO];
[newsView setTitle:#"News"];
image = [UIImage imageNamed: #"news"];
newsView.tabBarItem.image = image;
UINavigationController* navNew = [[UINavigationController alloc] initWithRootViewController:newsView];
[newsView release];
newsView = nil;
To make life easier on yourself I'd switch your project to use ARC so you have less to worry about WRT memory management.
Replace this line
image = [[UIImage alloc] initWithContentsOfFile:imgPath];
With
image = [UIImage imageWithContentsOfFile:imgPath];
and check once.
First, switch to ARC. There is no single thing you can do on iOS that will more improve your code and remove whole classes of memory problems with a single move.
Beyond that, the code above does not appear to have a leak itself. That suggests that the actual mistake is elsewhere. There are several ways this could happen:
You're leaking the IBNewsViewController somewhere else
IBNewsViewController messes with its tabBarItem incorrectly and leaks that
You're leaking the UINavigationController somewhere else
You're retaining the tabBarItem.image somewhere else and failing to release it
Those are the most likely that I would hunt for. If you're directly accessing ivars, that can often cause these kinds of mistakes. You should use accessors everywhere except in init and dealloc. (This is true in ARC, but is absolutely critical without ARC.)
Leak detection is not perfect. There are all kinds of "abandoned" memory that may not appear to be a leak. I often recommend using Heapshot (now "Generation") analysis to see what other objects may be abandoned; that may give you a better insight into this leak.
Why differences in iOS 6 vs iOS 7? I suspect you have the same problem on iOS 6, but it doesn't look like a "leak", possibly because there is something caching the image that was removed in iOS 7. The cache pointer may make it look like it's not a leak to Instruments.
Speaking of which, do make sure to run the static analyzer. It can help you find problems.
And of course, switch to ARC.
I have an animated GIF successfully loaded into an NSData or NSBitmapImageRep object. Reference for NSBitmapImageRep
I've figured how to return data like the number of frames in that gif using:
NSNumber *frames = [bitmapRep valueForProperty:#"NSImageFrameCount"];
However, I'm a bit confused as to how I can actually access that frame as its own object.
I think one of these two methods will help, but I'm not actually sure how they'll get the individual frame for me.
+ representationOfImageRepsInArray:usingType:properties:
– representationUsingType:properties:
Any help appreciated. Thanks
I've figured how to return data like the number of frames in that gif using:
NSNumber *frames = [bitmapRep valueForProperty:#"NSImageFrameCount"];
However, I'm a bit confused as to how I can actually access that frame as its own object.
To have access to a special frame indexOfFrame ( 0 <= indexOfFrame < [frames intValue] ) you only need to set the NSImageCurrentFrame and you are done. There is no need to use CG-functions or make copies of frames. You can stay in the object oriented Cocoa world. A small example shows the duration of all GIF frames:
NSNumber *frames = [bitmapRep valueForProperty:#"NSImageFrameCount"];
if( frames!=nil ){ // bitmapRep is a Gif imageRep
for( NSUInteger i=0; i<[frames intValue]; i++ ){
[bitmapRep setProperty:NSImageCurrentFrame
withValue:[NSNumber numberWithUnsignedInt:i] ];
NSLog(#"%2d duration=%#",
i, [bitmapRep valueForProperty:NSImageCurrentFrameDuration] );
}
}
Another example: write all frames of a GIF image as PNG files to the filesystem:
NSNumber *frames = [bitmapRep valueForProperty:#"NSImageFrameCount"];
if( frames!=nil ){ // bitmapRep is a Gif imageRep
for( NSUInteger i=0; i<[frames intValue]; i++ ){
[bitmapRep setProperty:NSImageCurrentFrame
withValue:[NSNumber numberWithUnsignedInt:i] ];
NSData *repData = [bitmapRep representationUsingType:NSPNGFileType
properties:nil];
[repData writeToFile:
[NSString stringWithFormat:#"/tmp/gif_%02d.png", i ] atomically:YES];
}
}
I've figured how to return data like the number of frames in that gif using:
NSNumber *frames = [bitmapRep valueForProperty:#"NSImageFrameCount"];
However, I'm a bit confused as to how I can actually access that frame as its own object.
As far as I know, you can't—not from an NSBitmapImageRep.
Instead, create a CGImageSource from the GIF data, and use CGImageSourceCreateImageAtIndex to extract each frame (preferably as you need it).
Alternatively, you might try setting the NSImageCurrentFrame property. If you need a rep for each frame, make as many copies as there are frames (minus one, since you have the original), and set each rep's current frame to a different number. But I haven't tried that, so I'm not sure it will actually work.
Basically, NSBitmapImageRep's GIF support is weird, so you should just use CGImageSource.
I think one of these two methods will help, but I'm not actually sure how they'll get the individual frame for me.
+ representationOfImageRepsInArray:usingType:properties:
– representationUsingType:properties:
No, those methods are for serializing an image (or image rep). They're for writing data out, not reading it in. (Notice what constants those methods expect in their type parameters.)
If you want to have a look at some working source code for a GIF decoder for iOS (works for MacOSX too) then you can find it AVGIF89A2MvidResourceLoader.m at github. The approach is to use the ImageIO framework and call CGImageSourceCreateWithData() along with CGImageSourceCreateImageAtIndex() to get access to the Nth gif image in the file. But, there are some tricky details related to detecting if a transparent pixel appears in the GIF and how to write the results to a file to avoid running out of memory if the GIF is really long that might not be obvious.
I am attempting to create an application that goes through various images from the net and aim to cache them onto the iPhone for offline use. The code I am currently working with is:
NSMutableDictionary *Cache;
- (UIImage *)CachedImage: (NSString*)url {
UIImage *image = [Cache objectForKey:url];
if (image == nil) {
image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:url]]];
[Cache setObject:image forKey:url];
//NSLog (#"Stored");
return image;
} else {
//NSLog (#"Taken");
return image;
} }
I call the function and place the image into an ImageView using the strip of code below.
[self.imageView setImage:[self CachedImage:url]]; // Change url to desired URL.
Using the NSLog, the problem I found is that the code doesn't actually store the value because the value is always reading nil. Why is that and are there other ways of storing images for offline use?
Thanks in advance.
-Gon
Use NSCache to cache UIImages. You can also save the image locally (if you reuse these images a lot and during multiple launch) so whenever your app closes or you flush your cache, you can get the images immediately from your local directory.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSCache_Class/Reference/Reference.html
You are looking for
NSCache
Check it out here: http://nshipster.com/nscache/
Poor NSCache, always being overshadowed by NSMutableDictionary in the
most inappropriate circumstances. It’s like no one knows its there,
ready to provide all of that garbage collection behavior that
developers take great pains to re-implement themselves.
My goal is to make a video out of a short sequence of opengl frames (around 200 frames). So in order to do this, I use the following code to create a array of images:
NSMutableArray* images = [NSMutableArray array];
KTEngine* engine = [KTEngine sharedInstance]; //Opengl - based engine
for (unsigned int i = engine.animationContext.unitStart; i < engine.animationContext.unitEnd ; ++i)
{
NSLog(#"Render Image %d", i);
[engine.animationContext update:i];
[self.view setNeedsDisplay];
[images addObject:[view snapshot]];
}
NSLog(#"Total image rendered %d", [images count]);
[self createVideoFileFromArray:images];
So this works perfectly fine on simulator, but not on device (retina iPad). So my guess is that the device does not support so many UIimages (specially in 2048*1536). The crash always happends after 38 frames or so.
Now as for the solution, I thought to create a video for each 10 frames, and then attached them all together, but when can I know if I have enough space (is the autorelease pool drained?).
Maybe I should use a thread, process 10 images, and fire it again for the next 10 frames once it's over?
Any idea?
It seems quite likely that you're running out of memory.
To reduce memory usage, you could try to store the images as NSData using the PNG or JPG format instead. Both PNG's and JPG's are quite small when represented as data, but loading them into UIImage objects can be very memory consuming.
I would advice you to do something like below in your loop. The autorelease pool is needed to drain the returned snapshot on each iteration.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *image = [view snapshot];
NSData *imageData = UIImagePNGRepresentation(image);
[images addObject:imageData];
[pool release];
This of course requires your createVideoFileFromArray: method to handle pure image data instead of UIImage objects, but that should probably be feasible to implement.
I have a panning gesture that pans across a series of images. I'm not sure how to correctly manage the memory for this, after panning for a certain length of time, I'm getting crashes.
animImage is a UIImageView;
Here is how it works:
- (IBAction) panAnim:(UIPanGestureRecognizer *)sender{
CGPoint translate = [sender translationInView:sliderView];
if (translate.x >= lastPoint) {
difference = translate.x - lastPoint;
[self forwardAnim:difference];
} else {
difference = lastPoint - translate.x;
[self backwardAnim:difference];
}
lastPoint = translate.x;
}
-(void)forwardAnim:(CGFloat)speed{
NSInteger newFrame = currentFrame+speed;
currentFrame = newFrame;
if (currentFrame>=totalFrames) {
currentFrame=0;
}
NSString *newImagePath = [NSString stringWithFormat:#"%#", [currentAnimation objectAtIndex:currentFrame]];
animImage.image = [UIImage imageNamed:newImagePath];
}
-(void)backwardAnim:(CGFloat)speed{
NSInteger newFrame = currentFrame-speed;
currentFrame = newFrame;
if (currentFrame<0) {
currentFrame=(totalFrames-1);
}
NSString *newImagePath = [NSString stringWithFormat:#"%#", [currentAnimation objectAtIndex:currentFrame]];
animImage.image = [UIImage imageNamed:newImagePath];
}
The animation detects position of the translate and calculates what 'frame' the animation should be at and then swaps out the image.
I'm getting a very smooth animation for this but obviously I'm causing a crash because I'm not managing the memory correctly. I'm getting Memory Warnings and then crashes - but only when I've been scrolling through the images for a while.
I need to figure out a way to pre-load 100 images at a time, so I can only keep the memory of 100 images. It's strange because the images are opened and closed properly in the IO Output in Instruments.
Thanks for your help!
Cheers, D
+[UIImageView imageNamed:] caches the images it loads, so it's not surprising that your memory usage keeps increasing. You might try changing those +imageNamed: calls to +imageWithContentsOfFile: instead. That may hurt your animation performance, but I suspect you'll see memory usage drop significantly.
A memory warning doesn't mean that a crash is imminent. It'd help if you'd post the details of the crash. Looking at the crash log, you should be able to identify a line in your code that leads to the crash -- it may be a few frames from the top of the stack. Find that line, put a breakpoint a line or two before it, and step through the code imagining what could happen on each line that might cause a problem -- failed allocation, bad value, etc.
BTW, your 25kB image files likely expand to something much larger in memory.