iOS - UIImageView animation memory leak? - iphone

I am currently using a UIImageView to animate a series of images depending on a button pressed by the user. I have about 5 different sets of images, that i have created in NSArrays, such as this:
-(void)initiateAnimations {
punchani = [NSArray arrayWithObjects:
[UIImage imageNamed:#"punch0001.png"],
[UIImage imageNamed:#"punch0002.png"],
[UIImage imageNamed:#"punch0003.png"],
[UIImage imageNamed:#"punch0004.png"],
[UIImage imageNamed:#"punch0005.png"],
[UIImage imageNamed:#"punch0006.png"],
[UIImage imageNamed:#"punch0007.png"],
[UIImage imageNamed:#"punch0008.png"],
[UIImage imageNamed:#"punch0009.png"],
[UIImage imageNamed:#"punch0010.png"],
[UIImage imageNamed:#"punch0011.png"],
[UIImage imageNamed:#"punch0012.png"],nil];
}
And then i am using the standard animation code to loop them once:
player.animationImages = punchani;
player.animationDuration = 0.50;
player.animationRepeatCount = 1;
[player startAnimating];
However, as i load more and more/different images into the UIImageView, i eventually get an output that a Memory warning as follows: (subsituted my actual executable with 'appname')
2013-03-15 20:52:23.065 AppName[2080:907] Received memory warning.
I knew this would probably happen, but ARC forbids me from releasing the arrays. How can i fix this leak? thanks

UIImage imageNamed: caches the images and will release the memory on it's own schedule. Use
+ (UIImage *)imageWithContentsOfFile:(NSString *)path to load memory directly
Try this answer as well. Remove array of images when animation is over

Related

How to assign separate memory spaces of same type of class

Hi my program adds small Images to main view. I have this undo button to remove recently added Image(subView). It works ok when it has all different Images, But when there are two same images it occurs error.
I think this is because it both points the same original png file. But I have no idea how to fix it. Please give me some hint.
add{
UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:#"pah%d",tagNum]];
TouchImageView *touchImageView = [[TouchImageView alloc] initWithFrame:imageRect];
imageCounter++;
touchImageView.tag = imageCounter;
touchImageView.image = image;
touchImageView.center = CGPointMake(160.0, 230.0);
[view addSubview:touchImageView];
}
undo{
[[self.view viewWithTag:imageCounter] removeFromSuperview];
imageCounter--;
}
I doubt that its your problem here but imageNamed: caches the image in memory with an internal caching system. Every time you ask for [UIImage imageNamed:#"foo"] you get the same UIImage instance.
You probably want to be using imageWithContentsOfFile: instead which returns a unique instance of a UIImage.
Try that and see if it makes a difference.
if you just have to remove recently added image...
then each time where you are adding the image store its reference like this - it will work well with ARC...
UIImageView *imageView = touchImageView;
then in your remove Recently added image button click's
for(UIImageView *iV in view.subviews)
{
if(iV == imageView)
{
[iV removeFromSuperView];
}
}
i think it will work ...

How to animate an image in iPhone app?

I have a simple image which I want to animate in up and down...How do I achieve it in iPhone app?
Please help
Thanks
You can use an array of images and animate them in 1 UIImage:
iOS 5:
#interface UIImage (WrightsCS)
+(UIImage *)animatedImageWithImages:(NSArray *)images duration:(NSTimeInterval)duration;
#end
if ( SYSTEM_VERSION_GREATER_THAN(#"5.0") )
{
NSArray *animationFrames = [NSArray arrayWithObjects:
[UIImage imageNamed:#"image_0.png"],
[UIImage imageNamed:#"image_1.png"],
[UIImage imageNamed:#"image_3.png"],
[UIImage imageNamed:#"image_4.png"], nil];
UIImage * animatedImage = [UIImage animatedImageWithImages:animationFrames duration:2.0f];
}
Have you looked at any of the Core Animation documents?
What I would do is to create a CALayer, add the image to its contents. position it just outside the view. add it as a sublayer to the the UIView's layer and then set the position of this layer to be where you want it to animate to.
That should give you enough of a hint to get started. When you get the hang of that you can look at doing more complicated animations.

iPhone app issue: UIImageView sequence not loading

First off, I am just starting with Xcode and iPhone developing, so please bear with me if these issues are redundant...I haven't found the answer yet, hence my posting. Second...ok, there is second...so I will get to the question.
I am using an UIImage view for the background image of my TheEyeViewController and I need another UIImageView to set on top of the background UIImageView. The foreground UIImageView needs to load a sequence of images...but it's not working. When I try to setup the foreground image view with code, I don't think the compiler is able to differentiate between the 2 image views. It doesn't crash...the sequence just doesn't show up.
Any help on this is greatly appreciated.
Thank you for your time.
//.h
UIImageView *imageView;
#property(nonatomic, retain) IBOutlet UIImageView *imageView;
//.m
- (void)viewDidLoad
{
imageView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"openingSeq1.jpg"],
[UIImage imageNamed:#"openingSeq2.jpg"],
[UIImage imageNamed:#"openingSeq3.jpg"],
[UIImage imageNamed:#"openingSeq4.jpg"],
[UIImage imageNamed:#"openingSeq5.jpg"],
[UIImage imageNamed:#"openingSeq6.jpg"],
[UIImage imageNamed:#"openingSeq7.jpg"],
[UIImage imageNamed:#"openingSeq8.jpg"],
[UIImage imageNamed:#"openingSeq9.jpg"],
[UIImage imageNamed:#"openingSeq10.jpg"],nil];
imageView.animationDuration = 2;
imageView.animationRepeatCount = 1;
[imageView startAnimating];
[self.view addSubview:imageView];
if(imageView.image == nil)
{
labelRandText.text = #"Images didn't load.";
}
[super viewDidLoad];
}
General comments that may or may not help:
Supposing [UIImage imageNamed:#"openingSeq1.jpg"] returns nil, that's the same as posting an empty array because that nil will look like the one that ends the list of things passed to arrayWithObjects:. It's probably worth adding a quick:
NSLog(#"os1: %#", [UIImage imageNamed:#"openingSeq1.jpg"]);
If that shows that you're getting nil back then your project is set up incorrectly, such that UIImage can't find the files.
If imageView is already in your view in Interface Builder then [self.view addSubview:imageView]; is redundant, but shouldn't be harmful.
An NSLog(#"%#", imageView) (or even one that logs both the imageView you're trying to reach and the other one that you think may be problematic, so you can check they're not the same one) can be used to verify that you have things wired up correctly in Interface Builder.
Also, technically you should call [super viewDidLoad]; before any of your own code, because logically you want the superclass to have done whatever it should do before you do whatever you should do. However, as with addSubview, this shouldn't really make any odds in your particular case because the UIViewController base class doesn't do anything in viewDidLoad.

(iphone) UIImageView setImage: leaks?

i'm changing image of UIImageview by [self setImage: newImage];
Looks like every time I does that with newImage, prior image doesn't seem to be released.
What's the correct way to replace image of UIImageView?
Thank you
Yes, UIImageView setImage does leak!
Actually, leaks CGImage, not UIImage (as instrument "allocation" shows)
I use BrutalUIImage instead of UIImage
#interface BrutalUIImageView : UIView {
UIImage *image;
}
#property(nonatomic, retain) UIImage *image;
#end
#implementation BrutalUIImageView
#synthesize image;
- (void)setImage:(UIImage *)anImage {
[image autorelease];
image = [anImage retain];
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
[image drawInRect:rect];
}
- (void)dealloc {
[image release];
[super dealloc];
}
#end
UIImageView setImage: never leaks, unless the image you are passing doesn't get released.
Your code wont leak, if your are assigning an autoreleased image to the image view, something like the following.
UIImage *newImage = [UIImage imageNamed:#"sampleImage"];
[yourImageView setImage:newImage];
But, if you are allocating the image somewhere, you have to release it manually.
Your BrutalUIImageVIew class is really interesting, but by drawing the Image using UIImage "drawInRect:" method, i loss the transparent areas of my PNG file.
Do you know how to draw the image, keeping the PNG transparence ?
(Of course, not using UIImageVIew wich leaks the CGImage while calling "setImage:")
Yes UIImageView setImage indeed leaks!
If you cycle through a bunch of images with
[yourImageView setImage:[UIImage imageNamed:#"sampleImage.png"]];
you can see on instruments memory usage increasing.
This seems to be some kind of caching going around since after
cycling through all the images memory usage will go flat.
The correct, or at least, the non leaky way to do it is:
NSString *thePath = [[NSBundle mainBundle] pathForResource:#"sampleImage" ofType:#"png"];
UIImage *newImage = [[UIImage alloc] initWithContentsOfFile:thePath];
[yourImageView setImage:newImage];
I verified this on my code as my APP was cycling through a lot
of large image files.

iPhone SDK: Does 3.0 clear the cache of a imageNamed? Because 2.2.1 doesn't!

I am creating an application for the iPhone which involves having more than one button which all animate the same UIImageView. It works well in the simulator, (like practically all apps) but when it comes to the device it plays the animation well, but with repetitive pressing of the buttons the app quits. So far I have implemented 2 buttons. Here's what happens when they are pressed.
Okay, I have 44 MB of ram available when my app starts, Then when I press the first of 2 buttons that start an animation the available memory goes down to 31 and climbs up to 32, then when I press the second button the available memory goes down to 9 then climbs to 24 and then strangely declines to 10 slowly. If the first button is then pressed then the available memory climbs to 14MB And under repetitive pressing of these buttons the apps memory goes down to 4 to 3 MB and quits. In instruments there are no leaks. Here is my code incase anyone can spot where my memory issue lurks. (BTW, I'm still developing on 2.2.1 because if I upgrade to 3.0 i won't be able to test my apps on the device as I am not enrolled in the apple developer program yet and I followed an online tutorial to get apps onto the device that only works with 2.2.1)
#synthesize 123pig;
- (IBAction)startClick:(id)sender{
animationTimer = [NSTimer scheduledTimerWithTimeInterval:(1.00/30.00) target:self selector:#selector(tick) userInfo:nil repeats:NO];
123pig.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed: #"123pigapple0001.png"],
[UIImage imageNamed: #"123pigapple0002.png"],
[UIImage imageNamed: #"123pigapple0003.png"],
[UIImage imageNamed: #"123pigapple0004.png"],
[UIImage imageNamed: #"123pigapple0005.png"],
[UIImage imageNamed: #"123pigapple0006.png"],
[UIImage imageNamed: #"123pigapple0007.png"],
[UIImage imageNamed: #"123pigapple0008.png"],
[UIImage imageNamed: #"123pigapple0009.png"],
[UIImage imageNamed: #"123pigapple0010.png"],
[UIImage imageNamed: #"123pigapple0011.png"],
[UIImage imageNamed: #"123pigapple0013.png"],
[UIImage imageNamed: #"123pigapple0014.png"],
[UIImage imageNamed: #"123pigapple0015.png"],
[UIImage imageNamed: #"123pigapple0016.png"],
[UIImage imageNamed: #"123pigapple0017.png"],
[UIImage imageNamed: #"123pigapple0018.png"],
[UIImage imageNamed: #"123pigapple0019.png"],
[UIImage imageNamed: #"123pigapple0020.png"],nil];
[123pig setAnimationRepeatCount:1];
123pig.animationDuration =.7;
[123pig startAnimating];
}
- (void)tick{
[self animatePig];
}
- (void)animatePig{
UIImage *pigImage13=[UIImage imageNamed:#"123pigapple0020.png"];
if(123pig.image == pigImage13)
123pig.image = pigImage13;
else
123pig.image = pigImage13;
}
- (IBAction)startClick1:(id)sender{
animationTimer1 = [NSTimer scheduledTimerWithTimeInterval:(1.00/30.00) target:self selector:#selector(tick1) userInfo:nil repeats:NO];
123pig.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed: #"123pig0015.png"],
[UIImage imageNamed: #"123pig0016.png"],
[UIImage imageNamed: #"123pig0017.png"],
[UIImage imageNamed: #"123pig0018.png"],
[UIImage imageNamed: #"123pig0019.png"],
[UIImage imageNamed: #"123pig0020.png"],
[UIImage imageNamed: #"123pig0021.png"],
[UIImage imageNamed: #"123pig0022.png"],
[UIImage imageNamed: #"123pig0023.png"],
[UIImage imageNamed: #"123pig0024.png"],
[UIImage imageNamed: #"123pig0025.png"],
[UIImage imageNamed: #"123pig0026.png"],
[UIImage imageNamed: #"123pig0027.png"],
[UIImage imageNamed: #"123pig0028.png"],
[UIImage imageNamed: #"123pig0029.png"],
[UIImage imageNamed: #"123pig0030.png"],
[UIImage imageNamed: #"123pig0031.png"],
[UIImage imageNamed: #"123pig0032.png"],
[UIImage imageNamed: #"123pig0033.png"],
[UIImage imageNamed: #"123pig0034.png"],
[UIImage imageNamed: #"123pig0035.png"],
[UIImage imageNamed: #"123pig0036.png"],
[UIImage imageNamed: #"123pig0037.png"],
[UIImage imageNamed: #"123pig0038.png"],
[UIImage imageNamed: #"123pig0039.png"],
[UIImage imageNamed: #"123pig0040.png"],
[UIImage imageNamed: #"123pig0041.png"],
[UIImage imageNamed: #"123pig0042.png"],
[UIImage imageNamed: #"123pig0043.png"],
[UIImage imageNamed: #"123pig0044.png"],
[UIImage imageNamed: #"123pig0045.png"],
[UIImage imageNamed: #"123pig0046.png"],
[UIImage imageNamed: #"123pig0047.png"],
[UIImage imageNamed: #"123pig0048.png"],
nil];
[123pig setAnimationRepeatCount:1];
123pig.animationDuration =2.7;
[123pig startAnimating];
}
- (void)tick1{
[self animatePig1];
}
- (void) animatePig1{
UIImage *pigImage11=[UIImage imageNamed:#"123pig0048.png"];
if(123pig.image == pigImage11)
123pig.image = pigImage11;
else
123pig.image = pigImage11;
}
- (void)stopTimer
{
[animationTimer invalidate];
[animationTimer release];
[animationTimer1 invalidate];
[animationTimer1 release];
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
123pig.center = location;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)dealloc {
[super dealloc];
[123pig release];
}
#end
I have an idea that it's imageNamed thats causing it and that 2.2.1 doesn't clear it's cache even after a memory warning, and on another SO question someone said "My understanding is that the +imageNamed: cache should respect memory warnings on iPhone OS 3.0. Test it when you get a chance and report bugs if you find that this is not the case." Now even if i did download the firmware and SDK of 3.0 I wouldn't know if it was clearing the cache of imageNamed because I wouldn't be able to test it on a device! BTW when answering keep in mind that I am new to The iPhone sdk. Thanks in advance.
----------------Edit----------------
I've just been accepted into the apple iphone developer program. WOOOHOOO, I'll check if 3.0 releases cached images properly.
-------------Edit------------------
Yes it does release cached images. Check my answer below for further clarification.
Okay people, It would seem that iPhone OS 3.0 does clear cached images on a did receive memory warning command, wooohooo! Me 1, Xcode 0. A bit more detail: In the iPhone os 2.2.1 when you release an animated Image view, the cached images aren't released, so if your app had loads of animations then eventually the iPhone would run out of memory and quit. So you would just have to go into other more complex methods of animation, and no one wants to do that if they can avoid it. But now releasing does clear the cache! I can tell because when I press a button in my app to animate a view there's a bit of a wait until the animation plays, then if that is pressed again there is no wait (because it cached it) But then if you press another button that animates that view, the previous cached animation is released! So pressing the first button gives you that original wait, so I can relax now and get on with developing rather then trying to fix a memory management issue that boils down to be apples fault.
I'm really confused about the behavior of your timers. You start the pig UIImageView animating, and then 1/30th of a second later, your timer fires and it sets the pig image to something else?
You may want to consolidate these two methods of animation (UIImageView animationImages and manual animation using timer). UIImageView really isn't meant to play long animations (see this question). I think it preloads all the images at once when you start the animation - thus the huge memory hit.
A better bet might be to use a timer for everything and keep a local variable corresponding to the current frame. When your timer fires, you could load just one image using imageNamed: and apply it to the UIImageView. I'm not sure what the caching behavior of UIImage is, but I'm pretty sure piling 100 of them into a UIImageView is a bad idea.
Is there any chance you could reduce the frame rate of your animation as well? It seems you're trying to flip through all 100 images in the pig's animation in 2.7 seconds - that's 37 fps! 24 fps would probably be fine and help lower your memory footprint.
Also - this is off topic, but I noticed you're calling [123pig dealloc] in your dealloc function. In general, you should just call release, and dealloc will be called automatically if the object's retain count is zero (meaning the object is no longer used). Manually calling it could cause you to destroy things that another object in the app is still using. Probably doesn't matter in this case, but it could cause some nasty bugs in other scenarios.
Sorry I don't have a definite answer on the cache issue. Hope that helps a bit!
Your dealloc code is incorrect. You should only be releasing 123pig, not deallocating it. You should do so before calling [super dealloc] since after that call, all your instance variables (incling 123pig) may be invalidated. This may have manifested itself as not releasing the image memory since there is a dangling reference to 123pig.