0

I'm using CAKeyFrameAnimation to animate few png images. I cut them from one png image by using CGImageCreateWithImageInRect

When I run Analyze function in XCode it shows me potential memory leaks. They are beause of CGImageCreateWithImageInRect. But when I use CGImageRelease after the animation object is created, no images are shown (I know why, but when I don't use release, there are leaks).

Could someone explain me this memory issue situation? And what is the best solution? I was thinking about creating UIImages with CGImageRefs for each "cut". But CAKeyFrameAnimation uses field of CGImageRef so I thought it is not necessary to create that UIImages.

UIImage *source = [UIImage imageNamed:@"my_anim_actions.png"];

cutRect = CGRectMake(0*dimForImg.width,0*dimForImg.height,dimForImg.width,dimForImg.height);
CGImageRef image1 = CGImageCreateWithImageInRect([source CGImage], cutRect);
cutRect = CGRectMake(1*dimForImg.width,0*dimForImg.height,dimForImg.width,dimForImg.height);
CGImageRef image2 = CGImageCreateWithImageInRect([source CGImage], cutRect);

NSArray *images = [[NSArray alloc] initWithObjects:(__bridge id)image1, (__bridge id)image2, (__bridge id)image2, (__bridge id)image1, (__bridge id)image2, (__bridge id)image1, nil];

CAKeyframeAnimation *myAnimation = [CAKeyframeAnimation animationWithKeyPath: @"contents"];
myAnimation.calculationMode = kCAAnimationDiscrete;
myAnimation.duration = kMyTime;
myAnimation.values = images; // NSArray of CGImageRefs
[myAnimation setValue:@"ANIMATION_MY" forKey:@"MyAnimation"];
myAnimation.removedOnCompletion = NO;
myAnimation.fillMode = kCAFillModeForwards;

CGImageRelease(image1);CGImageRelease(image2);//YES or NO
D33
  • 239
  • 1
  • 3
  • 14

2 Answers2

0

Two solutions:

First: Use [UIImageView animationImages] to animate between multiple images.

Second: Store the images as Ivars and release them in dealloc of your class.

Jonathan Cichon
  • 4,396
  • 16
  • 19
  • Thanks.I'm sorry for late reaction. 2, I may didn't understand it. My problem is when I do this every second : CAKeyframeAnimation *pom = [animations objectAtIndex:ANIMATION_WINK_NORMAL]; [imageView.layer removeAllAnimations]; [imageView.layer addAnimation:pom forKey:@"ANIMATION_WINK_NORMAL"]; in Instruments I can see that overallMemory is still growing.So it looks that when I call removeAllAnimations,it will leaks its "values".I think that when I alloc NSArray with CGImageRef values, it doesn't release it when NSArray is destroyed... Do you know what I mean? How can I avoid that leak. – D33 Sep 17 '12 at 15:42
  • And actually it is not possible to store every image as ivar. Because my example is very shortened. I mean I have more then ten CAKeyFrameAnimations stored in array and every animation is from 10 animation frames... it would be a LOT of ivars :) I'm very desperate about it. When I don't need the animation, I would like to destroy it (= nil) or something. When I use `(__bridge_transfer id)[UIImage imageWithCGImage:image1].CGImage` (where image1 is created like in my original question. In this way app crashes when I want to destroy anim. A when I use __bridge id, it leaks (i guess) – D33 Sep 18 '12 at 11:27
  • just to fix the too-many-ivar-problems you could use the `images` Array as ivar and call `CGImageRelease` on each entry when you are done. Two basic questions: Are you using ARC? If not, you have to release the `images` Array. Do you Debug on the Device or Simulator? The Simulator is not accurate, always use the Device. – Jonathan Cichon Sep 18 '12 at 11:35
  • Yes.I'm using ARC. So you mean to create ivar NSArray of UIImages or CGImageRefs?If I had NSArray of UIImages, destroy it would be easy.And for CAKeyFrameAnim I could use (id)UIImage.CGImage.Owner would be still UIImage.Am I right or totally wrong. And I'm using both.Simulator and Device.But the behavior is still the same.After some time of playing animations app starts to be very slow, animations are delayed and then it crashes. In Instruments is Live Bytes about 4.5MB, overall grows over hundreds MBs... :( – D33 Sep 18 '12 at 11:49
  • if your live-bytes stayes about 4.5MB i dont think your app is leaking images, because this should result in much larger living bytes. – Jonathan Cichon Sep 18 '12 at 11:55
  • Yes you could use UIImages to store your cgImages and still call `CGImageRelease` to clean the local variables. You can view all living objects of a specific type in instruments, take a look at the CGImage count. – Jonathan Cichon Sep 18 '12 at 12:06
  • In Instruments CGImage count and overall amount is constant. But CAKeyFrameAnimation is interesting. It grows... living bytes and #living is constant, but overall # and overall bytes is growing. But it can also be because of their images... :( And also CFArray and CFBasicHash grows. But I don't know these two...if they have any relationship with animations. – D33 Sep 18 '12 at 12:44
  • Hm, one thought. Is there i difference when I store "prepared" CAKeyFrameAnimations in NSArray and then simply use `[imageView.layer addAnimation:[animArr objectAtIndex:O] forKey:@"KEY"];` or when I create this animation instantly when I need it (and use only my "prepared" array with images?)? It looks that problem with overall growth is only when I "prepare" CAKeyFrameAnimation. When I create them instantly,it looks it doesn't increase overall memory... (or the problem hides very well). Could you please help me to clear this up? – D33 Sep 18 '12 at 14:08
  • Do you release the "prepared" Animations at somepoint? This is just an assumption on my part, but maybe the `CAKeyFrameAnimation` retains the layer it is added to and somehow holding the Animation keeps the layers alive the whole time your app runs. Or some other inconsistence occures during reusing the same animation on different layers. I dont think you will get a significant performance improvement if you "prepare" the Animations. – Jonathan Cichon Sep 18 '12 at 15:17
  • I don't release them (only on dealloc when app finishes).Because it's like a container of animations and I pick one when I need it (it has loaded images like frames which would take a lot of time during runtime).Then I pick one animation,when it finishes,I pick another one and the user can watch one continuous animation (but I put for example three together depending on the situation).But you had a good point with retaining the layer.In Instruments CALayer grows too.But I don't get why:/ I always use only one layer-`myUIImageView.layer` and I always do `myUIImageView.layer removeAllAnimations` – D33 Sep 18 '12 at 17:28
  • And it's not only about performance... every animation has its own duration or FillMode... So it's comfortable for me just pick it up by calling something like [myAnimation objectAtIndex:ANIMATION_WINK]... – D33 Sep 18 '12 at 17:32
  • during animations there may be copys of your layer (`layer.presentationLayer`). Try setting `animation.removedOnCompletion = YES`, maybe this prevents the app from releasing the presentationlayer after it is used. – Jonathan Cichon Sep 19 '12 at 07:57
0

Not sure if this helps, but I ran into something similar where using CGImageCreateWithImageInRect to take sections of an image and then showing those in image views caused huge memory usages. And I found that one thing that improved memory usage a lot was encoding the sub-image into PNG data and re-decoding it back into an image again.

I know, that sounds ridiculous and completely redundant, right? But it helped a lot. According to the documentation of CGImageCreateWithImageInRect,

The resulting image retains a reference to the original image, which means you may release the original image after calling this function.

It seems as though when the image is shown, the UI copies the image data, including copying the original image data that it references too; that's why it uses so much memory. But when you write it to PNG and back again, you create an independent image data, that is smaller and does not depend on the original. This is just my guess.

Anyway, try it and see if it helps.

user102008
  • 30,736
  • 10
  • 83
  • 104