Using ASIHTTP, the code below is in the ImageDownloader class. I get a memory leak, which is added at the bottom, but I don't know why. I thought tempImage would be autoreleased without me doing anything?
- (void)requestFinished:(ASIHTTPRequest *)request
{
UIImage *tempImage = [UIImage imageWithData:[request responseData]];
if (tempImage.size.width > 250.0f && tempImage.size.height > 180.0f)
{
self.image = tempImage;
self.circleImage = [UIImage imageNamed:#"hover.png"];
if ([self.delegate respondsToSelector:#selector(addImageToModel:)])
[self.delegate addImageToModel:self];
}
else
{
if ([self.delegate respondsToSelector:#selector(badImage)])
[self.delegate badImage];
}
tempImage = nil;
}
self.image is getting set to tempImage, so it probably retains the image. Is it released anywhere?
You should not write this line :
tempImage = nil;
Since tempImage is a function scope variable, it will be taking care of by itself.
Further more, if self.image is (nonatomic, assign) that could be the origine of your leak.
About the stack trace image, is this from leak instruments, or memory instruments ?
Unfortunately, until you give away some more code, we won't be able to help you further.
As Thomas mentioned, it may be self.image retaining the image?
Have you considered waking up the zombies ? ;)
http://www.mikeash.com/pyblog/friday-qa-2011-05-20-the-inner-life-of-zombies.html
http://www.cocoadev.com/index.pl?NSZombieEnabled
Related
In my applicayion i am using a uiimageview ,and it will load diffrent images on a button click. But there is memory leak when i load images, is that needed to release uiimageview.image property before i load another image to it. Any help please...........
code for loading images to uiimageview
-(void)setOverlayImage:(UIImage *)img
{
overlayView.image=nil;
overlayView.image=img;
}
Before i do overlayView.image=img; i hope the memory allocated for the previous image will be replaced with the new image.
Or is that needed to do [overlayView.image release] and then overlayView.image=img;???????
But when i tried to release, the app crashed.
-(void)setOverlayImage:(UIImage *)img
{
overlayView.image=img;
}
This should be sufficient but you can also go for this.
-(void)setOverlayImage:(UIImage *)img
{
if(overlayView)
{
[overlayView release];
overlayView = nil;
}
overlayView = [[UIImageView alloc] initWithImage:img];
overlayView.frame = yourFrame;
// Add this to your parent view
[self.view addSubview:overlayView];
}
Hope this helps
I am a beginner at this :)
I have an app which changes an image of a UIImageView when a button is pushed, each image is about 200 kb, when run in the simulator it runs ok, I can change the image many times with no problems.
When run on my device (ipod touch) it loads ok, it sluggishly gets through about 4 images and then it crashes. When I run it with the Allocations performance tool, it crashes when overall bytes reaches about 2.75 megs. Number of living allocations is about 8000 (is this high?).
My console reads this
Program received signal: “0”.
Data Formatters temporarily unavailable, will re-try after a 'continue'. (Unknown error loading shared library "/Developer/usr/lib/libXcodeDebuggerSupport.dylib")
I've also tried using the "leaks" performance tool and it doesn't find any leaks.
My .h file loads the image and uimageview like this:
#property(retain, nonatomic) IBOutlet UIImageView* myUIImageView;
#property(nonatomic, retain) UIImage *image;
Also, I release these like this:
- (void)dealloc {
[super dealloc];
[myUIImageView release];
[image release];
}
I also added in this, but it didn't seem to make any difference:
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
[myUIImageView release];
[image release];
// Release any cached data, images, etc that aren't in use.
}
I'm not sure what else to try at this point, any debugging techniques suggestions would be appreciated, also any pointers on how tackle memory issues like this would be hugely helpful,
thanks!
Also, sorry forgot to add the image changing code:
- (IBAction)myButton {
static NSUInteger counter = 0;
counter++;
if (counter == 1) {
myUIImageView.image = [UIImage imageNamed:#"image1.jpg"];
}
else {
myUIImageView.image = [UIImage imageNamed:#"image2.jpg"];
counter = 0;
}
}
Your image changing code looks fine, but I suggest these changes:
You should set myUIImageView to nil in your viewDidUnload method:
- (void)viewDidUnload {
// release retained subviews of main view
self.myUIImageView = nil;
}
In didReceiveMemoryWarning, you should set image to nil instead of sending a release:
self.image = nil;
Having [image release] in both didReceiveMemoryWarning and dealloc might cause a double release.
Also, don't release myUIImageView in didReceiveMemoryWarning.
Are you actually using the image ivar? It looks like you are assigning an image directly to the UIImageView. Having an unused ivar doesn't cause a problem, obviously, but I'm just wondering why it's there.
What code are you using to change the image? Make sure you are not re-alloc'ing the image or imageview.
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.
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. :-)
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 ;)