This is my code in my application,
[imageview setAlpha:1.0f];
[imageview setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%#.png",[pages objectAtIndex:swipeCount]]]];
[imageview setFrame:CGRectMake(-300, 0, 1368, 1000)];
Edit:
pages = [[NSArray alloc] initWithObjects:#"page1",#"page2",#"page3",#"page4",#"page5",#"page6",#"page7b",#"page8",#"page9",#"page10a",#"page11",#"page12",#"page13b",#"page14",#"page15",#"page16a",#"page17",#"page18",#"page19",#"page20",#"page21",#"page22",#"page23",#"page24",#"page25", nil];
imageview=[[UIImageView alloc]init];
its working properly, problems except when the app enters background and comes back to foreground shows the following error,
*** -[UIImage isKindOfClass:]: message sent to deallocated instance 0x1e8b10
What wrong with the code?
Please help me out
Yes, you need to allocate memory to your UIImage. What is basically happening is that your image is being temporarily stored in the memory and deallocated upon closing the app so iOS could allocate that memory to more immediate needs. You can fix that as below. I'm also gonna allocate a string since stringWithFormat returns an autorelease String.
NSString *imageName = [[NSString alloc] initWithFormat::#"%#.png",[pages objectAtIndex:swipeCount]];
UIImage *myImage = [UIImage imageNamed:myImage];
[imageview setImage:image];
Try using properties. Make array,imageView as a property and then use
self.pages and self.imageView
see this good article on properties
Objective-c properties
Related
I m loading lots of rather large images in my viewcontroller, using
NSUInteger nimages = 0;
for (; ; nimages++) {
NSString *nameOfImage_ = #"someName";
NSString *imageName = [NSString stringWithFormat:#"%#%d.jpg", nameOfImage_, (nimages + 1)];
image = [UIImage imageNamed:imageName];
if (image == nil) {
break;
}
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
//some other stuff....
[imageView release];
}
the usual unloading occurs in - (void)viewDidUnload and - (void)dealloc
with self.image = nil; and [image release];
It seems after a few "loading" and "unloading" the cache still grows to the point of no return!!
:)
and the app crashes...
any ideas??? how do i empty my cache? and where?
thanks
EDIT:
ok this is what i was doing wrong.
Apparently this piece of code fixes the whole caching problem:
image = [[UIImage imageNamed:imageName] autorelease];
with autorelease being the key here.
thanks for the replies...
Thank you all for your suggestions.
Solution:
Used ARC and imageWithContentsOffFile to initialize the Images.
image = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource:imageName ofType:nil]];
And Yes, imageNamed is only good for... well for nothing big...
image = [[UIImage imageNamed:imageName] autorelease];
This is incorrect. In keeping with the memory management rules, you shouldn't be releasing (or autoreleasing) the image because you didn't allocate or retain it. "imageNamed" doesn't contain "alloc", "new", "copy", or "retain".
As some of the other answers explain, you should load your images with a different method if you want more control over the memory they use.
imageNamed is an awful way to load images in reality, it never releases loaded images unless forced and keeps them in the cache forever. You should implement your own, more intelligent cache. A simple NSMutableDictionary gives the same functionality but with more flexibility.
For a more in-depth discussion you can read this: http://www.alexcurylo.com/blog/2009/01/13/imagenamed-is-evil/
Use another method to initialize you image. imageNamed caches.
Instead of using using imageNamed you can use imageWithContentsOfFile:
Or check this article
link 0
link 1
[[UIImage alloc] initWithContentsOfFile:path]
return nil when the method can't initialize the image. Then next code is not releasing the allocated UIImage, as image is nil in the [image release] line:
UIImage* image = [[UIImage alloc] initWithContentsOfFile:path];
if(image)
{
....
}
//Here image is nil and not releases allocated UIImage.
[image release];
Is this really a memory leak?
if init returns nil, how must release the object?
if I do
UIImage* image = [[UIImage alloc] initWithContentsOfFile:path];
and image is nil because init fails,
[image release] is the same as [nil release]. Ok there's not an error, but is not releasing anything.
Retain count in this example has nothing to do with whether or not the image is nil. You manually allocated the image using
UIImage* test = [UIImage alloc];
and therefore the retain count will be one until you manually release it, as you are the sole owner of that object.
See Memory Management Rules for more information on the subject.
release on nil is a no-op, so always ok. And it won't leak as you didn't have an object to start with.
UIImage* test = [UIImage alloc];
test is already an UIImage object on its own (though you failed to initialize it at this line).
You really should always do alloc/init on the same line (and on the same variable) - or the code logic is really hard to follow. Your code generates only one object, and then assigns it to another variable.
This is the same, though much clearer:
UIImage* test = [[UIImage alloc] initWithContentsOfFile:path];
UIImage* image = test;
int n = [test retainCount]
Here it is obvious that test and image are the same object (and hence have the same retainCount). Whenever you release one of them, the the object goes away (unless you retain it before).
Please also note that retainCount is not something you should rely on or make much assumptions on. It often is misleading at best.
I'm trying to use a url as a UIImage in the OpenFlow API.
NSString *imageUrl = [[[newsEntries objectAtIndex:0] objectForKey: #"still"] retain];
NSURL *url2 = [NSURL URLWithString:imageUrl];
NSData *photoData = [NSData dataWithContentsOfURL:url2];
UIImage *imageUI = [UIImage imageWithData:photoData]
UIImageView *myImage = [UIImageView initWithFrame:imageUI];
[(AFOpenFlowView *)self.view setImage:[UIImage imageNamed:myImage]];
[imageUr release];
[(AFOpenFlowView *)self.view setNumberOfImages:3];
I have tried it like this, but no success. The only way I got this API working was using the imageNamed type. The initwithData has no success.
So how can I change this NSString to finally become a imageNamed method?
A UIImageView is different from a UIImage.
Change this line: [(AFOpenFlowView *)self.view setImage:[UIImage imageNamed:myImage]];
To this: [(AFOpenFlowView *)self.view setImage:[UIImage imageNamed:imageUI]];
and it should work.
You have several significant errors here. It appears that you may need to read about C/Objective-C and types.
It sounds like you are asserting that, specifically, the line
UIImage *imageUI = [UIImage imageWithData:photoData]
is not working. The code up to that point actually looks okay (though it is not necessary to retain and then release the imageUrl variable.
Once you have created your UIImage, you should be able to pass it directly to your AFOpenFlowView:
[(AFOpenFlowView*)self.view setImage:imageUI forIndex:0];
The line
UIImageView *myImage = [UIImageView initWithFrame:imageUI];
has two errors (and is unnecessary anyway). First, -initWithFrame takes a CGRect as its argument, not a UIImage. UIImageView does have an initialization method -initWithImage, which is probably what you intended. But either way, methods that start with "init" are instance methods, not class methods, so you have to call them on actual instances of a UIImageView, like this:
UIImageView *myImage = [[UIImageView alloc] initWithImage:imageUI];
Note that this will leak memory unless you autorelease or release it.
The following line, you correctly try to give a UIImage to your AFOpenFlowView, but you attempt to create that UIImage by passing a UIImageView to the +imageNamed method. +imageNamed takes an NSString that contains the name of the image, and passing anything else to it won't work.
You must always be aware of what kind of object a method is expecting to receive, and make sure you find some way to give it that kind of thing.
Probably what you are looking for here is something like this:
NSString *imageUrl = [[newsEntries objectAtIndex:0] objectForKey: #"still"];
NSString *imageName = [imageUrl lastPathComponent];
[(AFOpenFlowView *)self.view setImage:[UIImage imageNamed:imageName]];
I'm implementing an image browser, using a UIScrollView. Due to memory costranints, I've to implement image dynamic loading (I don't want use CATiled Layers because it forces user remaining waiting to load every tile).
I've tried in a coupe of ways:
- (UIImageView*) ldPageView{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Top-level pool
NSError *error;
NSData *imData = [NSData dataWithContentsOfURL:ldPageRef options:NSDataReadingUncached error:&error];
UIImage *im = [[UIImage alloc] initWithData:imData];
ldView = [[UIImageView alloc] initWithImage:im] ;
[ldView setFrame:pageRect];
[pool release]; // Release the objects in the pool.
return ldView;
}
And even in this way
- (UIImageView*) ldPageView{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Top-level pool
CGDataProviderRef provider = CGDataProviderCreateWithURL ((CFURLRef)ldPageRef);
CGImageRef d = CGImageCreateWithJPEGDataProvider(provider,nil, true,kCGRenderingIntentDefault);
UIImage *im = [[UIImage alloc] initWithCGImage:d];
ldView = [[[UIImageView alloc] initWithImage:im] autorelease];
[im release];
CGDataProviderRelease(provider);
CGImageRelease(d);
[ldView setFrame:pageRect];
[pool release]; // Release the objects in the pool.
return ldView;
}
But every time I try it both on simulator and on iPad, memory explodes. I've runned my code with Instruments and no leak is reported. ldView is an istance variable and it is deallocated togheter with ldPageRef on object dealloc (which is called for sure).
I've also tried setting NSURLCache sharedCache to nil or to zero, but it is still happening.
I've read Memory management guide, but everythimg seems ok to me.
Please help
Try using
UIImage *im = [UIImage imageWithData:imData];
rather than
UIImage *im = [[UIImage alloc] initWithData:imData];
Always avoid allocs if possible otherwise you must ensure that you manually release the object.
More than likely it is how you are creating your UIImage. Try creating your image as such..
[UIImage imageWithData:imData];
instead of
[[UIImage alloc] initWithData:imData];
This will return a autoreleased object(it is a class method) so that you will not have to try to release it yourself later.
You are never releasing your alloc'd objects. You need to change:
[[UIImage alloc] initWithData:imData];
[[[UIImageView alloc] initWithImage:im];
to:
[[[UIImage alloc] initWithData:imData] autorelease];
[[[UIImageView alloc] initWithImage:im] autorelease] ;
Indeed I have found a memory leak in UIImageView. You just never pay any attention to it, since you may open images from the App package all the time, and these images are being cached by iOS.
But if you download a number of images from the network (say 40 iPhone-camera photos), save them to the documents and open them over and over again in a sub view controller, the memory leak applies. You do not need to have 40 different images, it is enough to load one image again and again.
Your test app is with ARC disabled and an image is being loaded from file and displayed in a sub view controller, every time that controller is being pushed.
In your sub view controller you'll create an UIImageView and assign the UIImage object to the image view's .image property. When you leave the sub view controller, you release the image view correctly. On an iPhone 4s your app won't open more than 80 images. Mine crashed at around 70 images (with iOS 6.1). And have a look on the intruments app, before the crash. The memory is full of CFMalloc blocks.
The solution I found is simple: Set the image property to nil, before you release the image view. Again, look at the instruments app. It now looks like what you'd expect and your app does no longer crash.
I think the same leak applies to whatever the UIWebView uses to display images and Apple doesn't know about it.
Im developing an app for an iPhone and I found that the following code is causing the memory allocation to increment.
-(UIImage *)createRecipeCardImage:(Process *)objectTBD atIndex:(int)indx
{
[objectTBD retain];
// bringing the image for the background
UIImage *rCard = [UIImage imageNamed:#"card_bg.png"];
CGRect frame = CGRectMake(00.0f, 80.0f, 330.0f, 330.0f);
// creating he UIImage view to contain the recipe's data
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = rCard;
[rCard release];
imageView.userInteractionEnabled = YES;
float titleLabelWidth = 150.0;
float leftGutter = 5.0;
float titleYPos = 25.0;
float space = 3.0;
float leftYPos = 0;
// locating Title label
float currentHeight = [self calculateHeightOfTextFromWidth:objectTBD.Title :titleFont :titleLabelWidth :UILineBreakModeWordWrap];
UILabel *cardTitle = [[UILabel alloc]initWithFrame:CGRectMake(leftGutter, titleYPos, titleLabelWidth, currentHeight)];
cardTitle.lineBreakMode = UILineBreakModeWordWrap;
cardTitle.numberOfLines = 0;
cardTitle.font = titleFont;
cardTitle.text = objectTBD.Title;
cardTitle.backgroundColor = [UIColor clearColor];
[imageView addSubview:cardTitle];
[cardTitle release];
leftYPos = titleYPos + currentHeight + space;
// locating brown line
UIView *brownLine = [[UIView alloc] initWithFrame:CGRectMake(5.0, leftYPos, 150.0, 2.0)];
brownLine.backgroundColor = [UIColor colorWithRed:0.647 green:0.341 blue:0.122 alpha:1.0];
[imageView addSubview:brownLine];
[brownLine release];
leftYPos = leftYPos + 2 + space + space + space;
// creating the imageView to place the image
UIImageView *processPhoto = [[UIImageView alloc] initWithFrame:CGRectMake(leftGutter, leftYPos, 150, 150)];
if((uniqueIndex == indx) && (uniqueImg.imageData != nil))
{
if([uniqueImg.rcpIden isEqualToString:objectTBD.iden])
{
objectTBD.imageData = [NSString stringWithFormat:#"%#", uniqueImg.imageData];
[recipesFound replaceObjectAtIndex:indx withObject:objectTBD];
NSData * imageData = [NSData dataFromBase64String:objectTBD.imageData];
UIImage *rcpImage = [[UIImage alloc] initWithData:imageData];
[imageData release];
processPhoto.image = rcpImage;
[rcpImage release];
}
}
else if(objectTBD.imageData != nil)
{
NSData * imageData = [NSData dataFromBase64String:objectTBD.imageData];
UIImage *rcpImage = [[UIImage alloc] initWithData:imageData];
processPhoto.image = rcpImage;
[rcpImage release];
[decodedBigImageDataPointers addObject:imageData];
}
else
{
UIImage * rcpImage = [UIImage imageNamed:#"default_recipe_img.png"];
processPhoto.image = rcpImage;
[rcpImage release];
}
NSlog(#" Process Photo Retain Count %i", [processPhoto retainCount]); // this prints a 1
[imageView addSubview:processPhoto];
NSlog(#" Process Photo Retain Count %i", [processPhoto retainCount]); // this prints a 2!!!!
//[processPhoto release]; // this line causes an error :(
// converting the UIImageView into a UIImage
UIGraphicsBeginImageContext(imageView.bounds.size);
[imageView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[objectTBD release];
for(UIView *eachSubview in imageView.subviews)
{
[eachSubview removeFromSuperview];
NSLog(#"each subview retainCount %i despues", [eachSubview retainCount]);
// here I find that the processPhoto view has a retain count of 2 (all other views have their retain count in 1)
}
return viewImage;
}
When I checked at the instruments object allocation I found that the "GeneralBlock-9216" growing up.
Breaking down the row I found that every time I call this code, one instance of:
2 0x5083800 00:18.534 ImageIO initImageJPEG
is being allocated. Checking the call stack, the following line is highlighted:
UIImage * objImage = [UIImage imageWithData:imageData];
Any help to find what the error is?
As TechZen said, the imageWithXXX: methods cache the image inside of them while you run the program (though you release the instances after using). I recommend initWithXXX: and release API sets instead of imageWithXXX:.
Well, if you embed several debug log on your source code, check how many times is it called, and check the retain count of the instances.
As far as I can explain, that is all.
I hope you will solve the problem.
Does anyone have an answer for this? It's tearing me apart trying to figure out why this image information keeps lingering. I've tried every solution.
The situation:
Images get downloaded and stored to the device, then loaded with imageWithContentsOfFile (or even initWithContentsOfFile, which doesn't help either). When the view goes away, the images don't, but they don't show up as leaks, they're just this initImageJPEG Malloc 9.00 KB that never goes away and keeps ramping up.
UPDATE: I believe I've figured this out: Check to make sure everything is actually getting dealloc'd when you're releasing whatever the parents (and/or grandparents) and etc of the images are. If the parents don't get deallocated, they never let go of their children images, and whatever data was in those images sticks around. So check retain counts of parent objects and make sure that everything's going away all the way up whenever you release the view at the top.
A good way to check for this is to put NSLogs into custom classes' dealloc methods. If they never show up, that object isn't going away, even though the reference to it might, and it (and whatever its subviews and properties are) will never ever disappear. In the case of images, this means a pretty sizable allocation every time that object is generated and never deallocated. It might not show up in leaks, especially if the parent of the topmost object you're thinking you're releasing but actually aren't persists and doesn't itself ever deallocate.
I hope this helps. It'll be useful to take some time to read through your code with a fine-toothed comb to make sure you're allocating and releasing things properly. (Search for "alloc]", start at the top of the file, and work your way down to make sure you're releasing and that the release isn't inside of some if() or something.)
Also, running "Build and Analyze" might lock up your machine for a bit, but its results can be really helpful.
Good luck!
I think you're seeing UIImage cacheing images. There used there used to be a method something like initWithData:cache that let you turn the cacheing off. Now I think the system always caches the images automatically behind the scenes even after you've deallocted the specific instances.
I don't think its an error on your part. I think it's the system keeping data around in the OpenGl subsystem. Unless it causes a major leak, I don't think it is a problem.