Free memory after iphone animation - iphone

My app crashes due to memory building up after every animation.
Here is my sample code:
-(IBAction) shoot: (id)delegate{
[gun setImage:[UIImage imageNamed:#"animation_machin_guns_320x480_7.png"]];
UIImage *frame1 = [UIImage imageNamed:#"animation_machin_guns_320x480_1.png"];
UIImage *frame2 = [UIImage imageNamed:#"animation_machin_guns_320x480_2.png"];
UIImage *frame3 = [UIImage imageNamed:#"animation_machin_guns_320x480_3.png"];
UIImage *frame4 = [UIImage imageNamed:#"animation_machin_guns_320x480_4.png"];
UIImage *frame5 = [UIImage imageNamed:#"animation_machin_guns_320x480_5.png"];
UIImage *frame6 = [UIImage imageNamed:#"animation_machin_guns_320x480_6.png"];
UIImage *frame7 = [UIImage imageNamed:#"animation_machin_guns_320x480_7.png"];
gun.animationImages = [[NSArray alloc] initWithObjects:frame1, frame2, frame3, frame4, frame5, frame6, frame7,nil];
gun.animationDuration = 1;
gun.animationRepeatCount = 1;
[gun startAnimating];
[frame1 release];
[frame2 release];
[frame3 release];
[frame4 release];
[frame5 release];
[frame6 release];
[frame7 release];}
Releasing the frames doesn't seem to do the magic.
I tried using this http://kosmaczewski.net/projects/iphone-image-cache/ for image caching but i guess I don't know how to use it properly since the memory builds up faster than using imageNamed.

Replace [[NSArray alloc] initWithObjects:xxx]; with [NSArray arrayWithObjects:xxx];.
Currently, the array object is being retained twice (once effectively by alloc and once by gun.animationImages = xxx), but only released once (when the gun object is released, or the animationImages property is set to something new), meaning it will never be released.
The arrayWithObjects method returns an autoreleased object, meaning it doesn't need to be manually released by you.

If I were you I would think about only animating parts of the screen or perhaps compressing the images into low quality jpgs or something along those lines.

Related

How to know when UIimageView finished loading?

In my view controller, how can I know when a certain UIImageView has finished loading (large jpeg from documents directory)? I need to know so that I can then swap a placeholder low-res imageview with this hi-res imageview. Do I need to create a custom callback to know this? Any way is fine.
By the way, here is a snippet of code where I load the image:
NSString *fileName = [NSString stringWithFormat:#"hires_%i.jpg", currentPage];
NSString *filePath = [NSString stringWithFormat:#"%#/BookImage/%#", [self documentsDirectory], fileName];
hiResImageView.image = [[[UIImage alloc] initWithContentsOfFile:filePath] autorelease];
UIImageView isn't doing any loading at all. All the loading is being done by [[UIImage alloc] initWithContentsOfFile:filePath], and your thread is blocked while the file is loaded (so the load is already complete by the time that call finally returns).
What you want to do is something like this:
- (void)loadImage:(NSString *)filePath {
[self performSelectorInBackground:#selector(loadImageInBackground:) withObject:filePath];
}
- (void)loadImageInBackground:(NSString *)filePath {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];
[self performSelectorOnMainThread:#selector(didLoadImageInBackground:) withObject:image waitUntilDone:YES];
[image release];
[pool release];
}
- (void)didLoadImageInBackground:(UIImage *)image {
self.imageView.image = image;
}
You would set up self.imageView to display the low-res image and then call loadImage: to load the high-res version.
Note that if you call this repeatedly before didLoadImageInBackground: gets called from earlier calls, you may cause the device to run out of memory. Or you might have the image from the first call take so much longer to load than image from the second call that didLoadImageInBackground: gets called for the second image before it gets called for the first. Fixing those issues is left as an exercise for the reader (or for another question).

NSAutoreleasePool leaking

I know this must be something simple that I am overlooking, but why is this leaking:
//add a pool for this since it is on its own thread
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//add the image
NSURL * imageURL = [NSURL URLWithString:[recipeData objectForKey:#"imagePath"]];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage * image = [UIImage imageWithData:imageData];
UIImageView * myImageView = [[UIImageView alloc] initWithImage:image];
//resize to make it fit the screen space
CGRect frame = myImageView.frame;
frame.size.width = 320;
frame.size.height = 357;
myImageView.frame = frame;
[self.view addSubview:myImageView];
[activity stopAnimating];
[pool drain];
[self placeItems];
I get the error:
_NSAutoreleaseNoPool(): Object 0x4e2fdf0 of class NSPathStore2 autoreleased with no pool in place - just leaking
I tried moving the placement of [pool drain] but that did nothing. I see a lot of code that looks just like this while searching Google for a cause.
Thanks for your help.
Draining the pool has the effect of releasing and subsequently deallocating it, since autoreleasepools cannot be retained. I suspect there must be some need for an autoreleasepool within placeItems (or some other place called after [pool drain]) since at that point the pool is propably gone already.
So, you might want to try commenting out the drain message to see if that will make the leak go away.
A lot of things to say here :
first, you're leaking myImageView. You have to release it after the -addSubview.
next, since you're on another thread, your [pool drain] must be at the end
last, since you're not on the main thread, you can't perform any UI operation. Try to replace [self.view addSubview:myImageView] by [self.view performSelectorOnMainThread:#selector(addSubview:) withObject:myImageView waitUntilDone:YES]. Same with [activity stopAnimating].
And like Brian said, the -drain message must be at the end of your thread.

Objective C: EXC_BAD_ACCESS when generating a UIImage

I have a view that generates an image based on a series of layers. I have images for the background, for the thumbnail, and finally for an overlay. Together, it makes one cohesive display.
It seems to work a dream, except for when it doesn't. For seemingly no reason, I get an EXC_BAD_ACCESS on the specified line below after it's generated somewhere between 8 and 20 images. I've run it through the memory leak tool and allocation tool, and it's not eating up tons of memory and it's not leaking. I'm totally stumped.
Here's the relevant code:
- (UIImage *)addLayer:(UIImage *)layer toImage:(UIImage *)background atPoint:(CGPoint)point {
CGSize size = CGSizeMake(240, 240);
UIGraphicsBeginImageContext(size);
[background drawAtPoint:CGPointMake(0, 0)]; // <--- error here
[layer drawAtPoint:point];
UIImage* result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
// Build the layered image -- thingPage onto thingBackground,
// then the screenshot on that, then the thingTop on top of it all.
// thingBackground, thingPage and thingTop are all preloaded UIImages.
-(UIImage *)getImageForThing:(Thing *)t {
[self loadImageCacheIfNecessary];
if (!t.screenshot) {
return [UIImage imageNamed:#"NoPreview.png"];
} else {
UIImage *screenshot = t.screenshot;
UIImage *currentImage = [self addLayer:thingPage toImage:thingBackground atPoint:CGPointMake(0, 0)];
currentImage = [self addLayer:screenshot toImage:currentImage atPoint:CGPointMake(39, 59)];
currentImage = [self addLayer:thingTop toImage:currentImage atPoint:CGPointMake(0, 1)];
return currentImage;
}
}
I can't find anywhere that this is going wrong, and I've been tearing my hair out for a couple of hours on this. It's the final known bug in the system, so you can imagine how antsy I am to fix it! :-)
Thanks in advance for any help.
As to me, I always use -(void)drawInRect: instead of -(void)drawAtPoint:
CGRect rtDraw;
rtDraw.origin = CGPointZero;
rtDraw.size = size;
[background drawInRect:rtDraw];
[layer drawInRect:rtDraw];
And ....
The paint method with UIGraphicsBeginImageContext(size) and UIGraphicsEndImageContext() is not thread-safe.
Those functions will push or pop a context with stack struct, which is managed by system.
EXC_BAD_ACCESS is almost always due to accessing an object that has already been released. In your code this seems to be t.screenshot. Check creation (and retaining if it is an instance variable) of the object returned by Thing's screenshot property.
As it turns out, the error wasn't in the code I posted, it was in my caching of the thingBackground, thingPage and thingTop images. I wasn't retaining them. Here's the missing code, fixed:
-(void)loadImageCacheIfNecessary {
if (!folderBackground) {
thingBackground = [[UIImage imageNamed:#"ThingBack.png"] retain];
}
if (!folderPage) {
thingPage = [[UIImage imageNamed:#"ThingPage.png"] retain];
}
if (!folderTop) {
thingTop = [[UIImage imageNamed:#"ThingTop.png"] retain];
}
}
I will admit I'm still not comfortable with the whole retain/release/autorelease stuff in Objective C. Hopefully it'll sink in one day soon. :-)

iPhone app memory leak with UIImage animation? Problem testing on device

I have an animation which works fine in the simulator but crashes on the device. I am getting the following error...
Program received signal: “0”.
The Debugger has exited due to signal 10 (SIGBUS)
A bit of investigating suggests that the UIImages are not getting released and I have a memory leak. I am new to this so can someone tell me if this is the likely cause? If you could also tell me how to solve it then that would be amazing.
The images are 480px x 480px and about 25kb each. My code is below...
NSArray *rainImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"rain-loop0001.png"],
[UIImage imageNamed:#"rain-loop0002.png"],
[UIImage imageNamed:#"rain-loop0003.png"],
[UIImage imageNamed:#"rain-loop0004.png"],
[UIImage imageNamed:#"rain-loop0005.png"],
[UIImage imageNamed:#"rain-loop0006.png"],
//more looping images
[UIImage imageNamed:#"rain-loop0045.png"],
[UIImage imageNamed:#"rain-loop0046.png"],
[UIImage imageNamed:#"rain-loop0047.png"],
[UIImage imageNamed:#"rain-loop0048.png"],
[UIImage imageNamed:#"rain-loop0049.png"],
[UIImage imageNamed:#"rain-loop0050.png"],
nil];
rainImage.animationImages = rainImages;
rainImage.animationDuration = 4.15/2;
rainImage.animationRepeatCount = 0;
[rainImage startAnimating];
[rainImage release];
Thanks
I'm pretty sure you're crashing because [NSArray arrayWithObjects:] returns an auto-released object which you're storing to rainImage. Then at the bottom you're releasing rainImage again, thus over releasing it.
The root of the problem is that you are running out of main memory because you have too many decompressed images in memory, see my answer to uiimage-animation-causing-app-to-crash-memory-leaks for the reason you should not be using UIImageView.animationImages.

UIImage within Thread not being Released / Overwritten

This appears to be the the classic method for scanning images from the iPhone. I have a thread that is dispatched from the main thread to go and scan for Codes. It essentially creates a new UIImage each time then removes it.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
{
while (![thread isCancelled]) {
#ifdef DEBUG
NSLog(#"Decoding Loop");
#endif
// [self performSelectorOnMainThread:#selector(updateImageBuffer) withObject:nil waitUntilDone:YES];
CGImageRef cgScreen = UIGetScreenImage();
UIImage *uiimage = [UIImage imageWithCGImage:cgScreen];
if (uiimage){
CGSize size = [uiimage size];
CGRect cropRect = CGRectMake(0.0, 80.0, size.width, 360); // Crop to centre of the screen - makes it more robust
#ifdef DEBUG
NSLog(#"picked image size = (%f, %f)", size.width, size.height);
#endif
[decoder decodeImage:uiimage cropRect:cropRect];
}
[uiimage release];
CGImageRelease(cgScreen);
}
}
[pool release];
the problem is that the [pool release] causes an ERROR_BAD_EXC (that old classic) and the program bombs. I'm told that there is no need to call [uiimage release] as I havent explicitly allocated a UIImage but this doesn't seem to be the case. If I take that line out, Memory usage goes through the roof and the program quits dues to lack of memory. It appears I can't have this work the way I'd like.
Is there a way to create a UIImage "in-place"? I.e, have a buffer that is written to again and again as a UIImage? I suspect that would work?
Update!
Tried executing the UIKit related calls on the main thread as follows:
-(void)performDecode:(id)arg{
// Perform the decoding in a seperate thread. This should, in theory, bounce back with a
// decoded or not decoded message. We can quit at the end of this thread.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
{
while (![thread isCancelled]) {
#ifdef DEBUG
NSLog(#"Decoding Loop");
#endif
[self performSelectorOnMainThread:#selector(updateImageBuffer) withObject:nil waitUntilDone:YES];
if (uiimage){
CGSize size = [uiimage size];
CGRect cropRect = CGRectMake(0.0, 80.0, 320, 360); // Crop to centre of the screen - makes it more robust
#ifdef DEBUG
NSLog(#"picked image size = (%f, %f)", size.width, size.height);
#endif
[decoder decodeImage:uiimage cropRect:cropRect];
}
}
}
[pool drain];
#ifdef DEBUG
NSLog(#"finished decoding.");
#endif
}
-(void) updateImageBuffer {
CGImageRef cgScreen = UIGetScreenImage();
uiimage = [UIImage imageWithCGImage:cgScreen];
//[uiimage release];
CGImageRelease(cgScreen);
}
No joy however as EXC_BAD_ACCESS rears its ugly head when one wishes to grab the "Size" of the UIImage
As has been stated by others, you should not release the UIImage returned from imageWithCGImage: . It is autoreleased. When your pool drains, it tries sending a release message to your already-released image objects, leading to your crash.
The reason why your memory usage keeps climbing is that you only drain the autorelease pool outside of the loop. Your autoreleased objects keep accumulating inside of the loop. (By the way, you need to release your autorelease pool at the end of that method, because it is currently being leaked.) To prevent this accumulation, you could drain the pool at regular intervals within the loop.
However, I'd suggest switching to doing [[UIImage alloc] initWithCGImage:cgScreen] and then releasing the image when done. I try to avoid using autoreleased objects whereever I can within iPhone applications in order to have tighter control over memory usage and overall better performance.
UIGetScreenImage() is private and undocumented so you flat-out cannot use it. Saying that nothing about it suggests that you now own CGImageRef cgScreen so why do you release it? You also have no way of knowing if it is thread safe and so should assume it isn't. You then go on to release the IImage *uiimage which you did not init, retain or copy, so again - you don't own it. Review the docs.
[uiimage release] is definitely wrong in this context. Also, Apple stresses that all UIKit methods must be executed on the main thread. That includes UIGetScreenImage() and +[UIImage imageWithCGImage:].
Edit: So you get an exception when calling -[UIImage size] on the wrong thread. This probably shouldn't surprise you because it is not permitted.
UIImage *uiimage = [[UIImage alloc] initWithCGImage: cgScreen];
Explicitly stating that I know best when to release the object seemed to work. Virtual Memory still increases but physical now stays constant. Thanks for pointing out the UIKit Thread Safe issues though. That is a point I'd missed but seems not affect the running at this point.
Also, I should point out, Red Laser and Quickmark both use this method of scanning camera information ;)