(iphone) UIImageView setImage: leaks? - iphone

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.

Related

iOS - UIImageView animation memory leak?

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

UIImagePickerController image,Display to another class

I want to appear in the "UIImageView" of "PreviewViewController" pictures taken with "ViewController".
But I can not.
Please tell me
ViewController.m
-(void)imagePickerController:(UIImagePickerController *)pic didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[imagePicker dismissViewControllerAnimated:YES completion:NO];
UIImage *originalImage;
originalImage = [info objectForKey:UIImagePickerControllerOriginalImage];
CGSize size = { 100, 200 };
UIGraphicsBeginImageContext(size);
CGRect rect;
rect.origin = CGPointZero;
rect.size = size;
[originalImage drawInRect:rect];
UIImage* shrinkedImage;
shrinkedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGRect r = CGRectMake(0, 60, 100, 100);
CGImageRef cgImage = CGImageCreateWithImageInRect(shrinkedImage.CGImage, r);
iconImage = [UIImage imageWithCGImage:cgImage];
UIImageWriteToSavedPhotosAlbum (iconImage, nil, nil , nil);
// This problem!!!
PreviewViewController *p = [[PreviewViewController alloc] init];
p.imagePreview.image = iconImage;
[self presentViewController:p animated:YES completion:NO];
}
PreviewViewController.h
#property (nonatomic) UIImageView *imagePreview;
Have you created a UIImageView that can receive your iconImage?
At first glance I would say that p.imagePreview == nil when you are trying to assign an image to it.
If it was created in a .xib, make sure the outlet is connected.
From your comment I understand that you are creating your UIImageView in viewDidLoad, which is often not call before the view is asked to go to the screen (there are ways to force it to append sooner).
Which means that p.imagePreview is probably nil when you are assigning your image there.
Have you try to put a breakpoint or to NSLog(#"%#", p.imagePreview); to see if the instance is valid or not?
If it is nil at that point I can think of at least 2 ways of getting around that.
You need to revise the UIViewController Life cycle to understand why your p.imagePreview is nil.
I suggest that you put NSLog() at the start of every method of your UIViewController life cycle method. ( All the viewDid..., viewWill..., ) to have a clear picture of the life cycle and understand when thing get call by the system.
As for your problem you could define #property (nonatomic, strong) UIImage *anImage and store the image you want to use in your image view there and use that image when initializing your UIImageView in viewDidLoad.
Since you are not using a .xib you should also consider overwriting loadView.

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 ...

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.

stretchableImageWithLeftCapWidth:topCapHeight doesn't work in initWithCoder: of UIImageView subclass

I have a UIImageView subclass called ShadowView, which displays a shadow that can be used under anything. ShadowViews are to be loaded from a nib.
In initWithCoder:, I have the following code:
- (id)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (self != nil) {
UIImage *shadowImage = [[UIImage imageNamed:#"drop_shadow_4_pix.png"] stretchableImageWithLeftCapWidth:4 topCapHeight:4];
[self setContentMode:UIViewContentModeScaleToFill];
[self setImage:shadowImage];
}
return self;
}
When I run the app, though, this image does not appear.
But if I change it to
...
UIImage *shadowImage = [UIImage imageNamed:#"drop_shadow_4_pix.png"];
...
it works fine, but it is stretched wrong.
Any ideas as to why this is happening?
Edit: it is the same when I load the shadowview programmatically, with initWithFrame: implemented similarly to initWithCoder:.
Another Edit: I think I solved the problem. I needed to set the autoresizing masks.
Is shadowImage nil?
UIImage *shadowImage = [[UIImage imageNamed:#"drop_shadow_4_pix.png"] stretchableImageWithLeftCapWidth:4 topCapHeight:4];
That method could return nil if the base image is less than 5 pixels wide or 5 pixels tall since it needs the 4 pixels for the caps + 1 pixel to stretch.