Memory leak not detected by static analyzer - objective-c

I came across this piece of code
UIImageView *image = [[UIImageView alloc] initWithFrame:imageFrame];
[image setImage:[UIImage imageNamed:#"myImage"]];
[self.view addSubview:image];
image = nil;
Given that ARC is not used, I assume that it will lead to a memory leak on the image object.
Nevertheless the static analyzer doesn't catch that.
I'm wondering who is mistaken, the static analyzer or me, and I'd like a second opinion on that.
Does the above code actually leak?

It turned out the one mistaken was me (duh!)
The application I'm auditing is pretty large, and I missed the fact that the developers enabled ARC with the -fobjc-arc flag on specific classes, including the one the above snippet is taken from.
Long live to the static analyzer!

self.view has an array of pointers to its subviews that will keep your image accessible and keep the reference counter (used for ARC) above 0.
CGRect imageFrame = CGRectMake(100, 100, 100, 200);
UIImageView *image = [[UIImageView alloc] initWithFrame:imageFrame];
NSLog(#"image=%#", image);
[image setImage:[UIImage imageNamed:#"Default"]];
[self.view addSubview:image];
image = nil;
UIView *v = [[self.view subviews] objectAtIndex:0];
NSLog(#"v=%#", v);
When you put self.view = nil; the reference counter for image will drop and the memory will be free'd. I don't think you can create a leak this way :)

Related

ImageView Animation getting more Memory

My application is in ARC.
I am using image Animation in imageView but after some period of time my application crashed with low memory error.
I am not getting any error message in Log but in Profile tool i am getting "Low Memory" message in allocation Part.
One code of my Animation is
ImageView= [[UIImageView alloc] init];
[self.view addSubview:ImageView];
ImageView.frame=CGRectMake(0, 0, 480, 342);
ImageView.animationImages = [NSArray arrayWithObjects:
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"image1" ofType:#"png"]],
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"image2" ofType:#"png"]],
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"image3" ofType:#"png"]],
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"image4" ofType:#"png"]],
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"image5" ofType:#"png"]],
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"image6" ofType:#"png"]],
nil];
[ImageView setAnimationRepeatCount:1];
ImageView.animationDuration =1.0;
[ImageView startAnimating];
If i comment this animation code then application is running correctly but i need more than 5 this type of animation in one view.
Any Link, tutorial, any Idea will be great help...
Why don't you try with a sprite? I've tried to animate with CALater (Core Animation Layer) and it works like a charm. I animate the 'contentsRect'. Short code example (CALayer subclass):
self.contents = [UIImage imageNamed:#"sprite-large.png"].CGImage;
NSMutableArray *frames = [NSMutableArray array]; // array of CGRect's
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"contentsRect"];
anim.values = frames;
[self addAnimation:anim forKey:nil];
I've posted some code to GitHub a few weeks ago:
https://github.com/Gerharbo/sprite-animation-layer/
It provides an example sprite (not perfect, but it works) and some example code.
Have fun animating!
Gerhard
Gerharbo's method is basically a texture atlas but with one much bigger image. What I think you will find is that this kind of approach does not scale well when you try to use many images (for a long animation). You are already seeing crashes caused by running out of memory, so it would be a good idea to completely avoid UIImageView.animationImages. This blog post video-and-memory-usage-on-ios-devices describes why you cannot allocate memory to hold a bunch of images in main memory, which is exactly what you code above is trying to do. If you look around on SO you will find many other developers have ran into this same issue.

Removing UIImageView and getting the memory back (my memory is leaking)

I am making a simple drawing application, but a have problems with memory. After a few minutes using it, it just collapse, it makes the iPad very slow.
The lines the user create drawing are made of a UIImageView of 2x2 pixel, so it's this UIImageView is created several times:
UIImageView*dibujo = [[UIImageView alloc] initWithFrame:CGRectMake(x-1,y-1,2,2)];
dibujo.image = [UIImage imageNamed:#"pixelde2po2.png"];
tagdedibujo=tagdedibujo+1;
dibujo.tag=tagdedibujo;
[self.view addSubview:dibujo];
And it's being erased in this way:
for (int aa=ultimotag+1; aa<=tagdedibujo; aa++) {
//NSLog(#"destruyendo: %i", aa);
UIImageView*dibujo1 = (UIImageView *)[self.view viewWithTag:aa];
[UIView animateWithDuration:0.5 animations:^{
dibujo1.alpha=0;
}];
}
[self performSelector:#selector(matardibujo) withObject:(self) afterDelay:(0.51)];
ultimotag=tagdedibujo;
-(void) matardibujo{
for (int aa=ultimotag+1; aa<=tagdedibujo; aa++) {
[[self.view viewWithTag:aa] removeFromSuperview];
}
}
Even after erasing de draw (matardibujo) i don't get the memory back. Even it grows. So i think the erasing part is the one that's messing here.
I would preciate your help, i'm kind of new in this. Thanks :).
Could you replace dibujo.image = [UIImage imageNamed:#"pixelde2po2.png"]; with the following line of code to see if it resolves your issue?
NSString *fileName = [[NSBundle mainBundle] pathForResource:#"pixelde2po2" ofType:#"png"];
UIImage *image = [UIImage imageWithContentsOfFile:fileName];

iOS UIAppearance Error

I'm building a file management app, and I occasionally get the following error while calling a UIImagePickerController or a MPMediaPickerController:
*** -[_UIImageViewPretiledImageCacheKey hash]: message sent to deallocated instance 0x140dc0
I recently gave my app a custom theme using iOS 5's UIAppearance API and thats when I started getting this error. By guessing and checking, I found the problematic lines of my code that cause this error:
UIImage *backButtonImage = [[UIImage imageNamed:#"backButton.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(12, 16, 12, 8)];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:backButtonImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
UIImage *barButtonImage = [[UIImage imageNamed:#"barButton.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(14, 12, 14, 12)];
[[UIBarButtonItem appearance] setBackgroundImage:barButtonImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
I have no idea how this code triggers the above error. Can you please explain to me the source of this error and provide a solution to fix it.
Thanks in advance for your help,
Guvvy
After some more thorough testing, I have reached the conclusion that this problem is limited to retina devices. The issue turned out to be in the #2x images. They had a odd numbered resolution (eg. 59px by 60px). All I did was recreate the image and changed the resolution to 60px by 60px and I never experienced the problem again.
I was kind of surprised by the solution as I saw no correlation between the error message and the line of code, but in the end, it was the images that caused this problem.
I had a similar problem, but my crash was caused by a resizable image in a UIImageView.
My resizable image has edge insets of top=30px, left=20px, bottom=1px, right=10px. The image is 43x45, so its resizable area is 13x14. I'm using iOS6, so I was able to workaround the problem by specifying UIImageResizingModeStretch as the resizingMode for -[UIImage resizableImageWithCapInsets:resizingMode:].
Works:
UIImage *image = [UIImage imageNamed:name];
UIImage *resizableImage = [image resizableImageWithCapInsets:edgeInsets resizingMode:UIImageResizingModeStretch];
Crashes with EXC_BAD_ACCESS or [_UIImageViewPretiledImageCacheKey hash]: message sent to deallocated instance 0xb14deb0 with NSZombieEnabled:
UIImage *image = [UIImage imageNamed:name];
UIImage *resizableImage = [image resizableImageWithCapInsets:edgeInsets];
I got the same for the following code
UIImage* image = [UIImage stretchableImageNamed:#"imageName"];
self.backgroundView = [[UIImageView alloc] initWithImage:image];
self.selectedBackgroundView = [[UIImageView alloc] initWithImage:image];
where self is UITableViewCell and stretchableImageNamed: is simply
+(UIImage*)stretchableImageNamed:(NSString*)name
{
UIImage *img = [UIImage imageNamed:name];
CGSize sz = img.size;
int left = (sz.width - 1.) / 2.;
int top = (sz.height - 1.) / 2.;
return [img resizableImageWithCapInsets:UIEdgeInsetsMake(top, left, top, left)];
}
this helped:
self.backgroundView = [[UIImageView alloc] initWithImage:[UIImage stretchableImageNamed:#"imageName"]];
self.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage stretchableImageNamed:#"imageName"]];

Cocoa Touch: Adding images in for loop

this is crashing on me when I put in an array of image urls, I think its because of me creating the UIImageView and then trying to do it again with the same name!
- (void)cycleImages:(NSArray *)images {
int fromLeft = 5;
for(int n = 0; n <= [images count]; n++){
UIImageView *myImageView = [[UIImageView alloc] initWithImage:[[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[images objectAtIndex:n]]]]];
myImageView.frame = CGRectMake(fromLeft, 5, 48, 48); // from left, from top, height, width
fromLeft = fromLeft + 53;
//add image view to view
[self.view addSubview:myImageView];
NSLog(#"%#", [images objectAtIndex:n]);
}
}
Any ideas?
Thanks!
Dex
First, you should use fast enumeration instead of an index loop. Fast enumeration is simpler, clearer, and faster—whenever you don't specifically need an index for something, there is no reason not to use fast enumeration.
UIImageView *myImageView = [[UIImageView alloc] initWithImage:[[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[images objectAtIndex:n]]]]];
This assumes that [images objectAtIndex:n] is a string. Is that what you put into the array?
Also, because you created that image view with alloc, you need to release it. If you're using ARC, that's done automatically, so you don't need to do anything; otherwise, you need to either send it release after adding it as a subview or send it autorelease.
this is crashing on me …
Please be more specific. What sort of “crash” do you get, and on what line?
If you get a crash log, please edit your question to include it.

Creating a method for wrapping a loaded image in a UIImageView

I have the following un my applicationDidFinishLaunching method
UIImage *image2 = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"image2.jpg" ofType:nil]];
view2 = [[UIImageView alloc] initWithImage:image2];
view2.hidden = YES;
[containerView addSubview:view2];
I'm simply adding an image to a view. But because I need to add 30-40 images I need to wrap the above in a function (which returns a UIImageView) and then call it from a loop.
This is my first attempt at creating the function
-(UIImageView)wrapImage:(NSString *)imagePath
{
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:imagePath
ofType:nil]];
UIImageView *view = [[UIImageView alloc] initWithImage:image];
view.hidden = YES;
return view;
}
And then to invoke it I have the following so far, for simplicity I am only wrapping 3 images
//Create an array and add elements to it
NSMutableArray *anArray = [[NSMutableArray alloc] init];
[anArray addObject:#"image1.jpg"];
[anArray addObject:#"image2.jpg"];
[anArray addObject:#"image3.jpg"];
//Use a for each loop to iterate through the array
for (NSString *s in anArray) {
UIImageView *wrappedImgInView=[self wrapImage:s];
[containerView addSubview:wrappedImgInView];
NSLog(s);
}
//Release the array
[anArray release];
I have 2 general questions
Is my approach correct?, ie following best practices, for what I am trying to do (load a multiple number of images(jpgs, pngs, etc.) and adding them to a container view)
For this to work properly with a large number of images, do I need to keep my array creation separate from my method invocation?
Any other suggestions welcome!
Just a note, in function declaration you should return pointer to UIImageView, not an UIImageView itself (i.e. add an asterisk).
Also, when returning the view from the function you should autorelease it. Otherwise it will leak memory. So initialization should look something like this:
UIImageView *view = [[[UIImageView alloc] initWithImage:image] autorelease];
Everything else seems to look good.