I have been having a tough time with memory consumption in a hefty app. I've got rid of almost all memory leaks. One section has a zoomable UIScrollView of a map that's pretty large: 2437x1536. It chooses between pngs in an array. Before I was using +imageNamed:, but I heard that this can make apps sometimes run poorly because it may keep the image in cache, which can consume more memory even if you're out of the view that was using it. Now I'm using +imageWithData:. The app hasn't crashed yet, but upon the 4th or 5th time of launching the map section, only some of the image appears, and there's flickering black areas. It didn't happen before with imageNamed. Sometimes it entirely disappears except for just a rectangular upper corner, and I go back to another view, and an image is flashing there too.
Here's what I have to display the map image. It's in a view's -initWithFrame: method:
mapList = [[NSMutableArray alloc] init];
[mapList addObject:#"Pacific_Map"];
[mapList addObject:#"Atlantic_Map"];
NSString *mapFileLocation = [[NSBundle mainBundle] pathForResource:[map_List objectAtIndex:mapNum] ofType:#"png"];
NSData *mapIMGData = [NSData dataWithContentsOfFile:mapFileLocation];
mapImageView = [[UIImageView alloc] initWithImage:[UIImage imageWithData:mapIMGData]];
Anything obvious that would be causing this effect?
Sorry I was allocating an image for the external screen twice. This still eventually makes it crash. I thought I'd be not caching the image with this technique.
Related
I've an application which is a scrollView filled with over 150 images .. I've followed this tutorial to create it .. the application is over 550MBs and it has about 500 (150 for iPhone 5 & 150 for Retina & 150 for non-retina & buttons) photos .. The application runs very well on the simulator with no problems but on a real device when I open the application it keeps loading then a crash
so can anyone help me with this, please?
Thanks in advance.
Try to load only those images needed for displaying. ie while scrolling those images that are hidden should be taken off from the scrollview. Maybe you can make a design similar to the working of reusable UITableViewCell. ie a custom implementation for showing only the needed images and reusing them while scrolling.
There is another way, not a straight forward one, you can use a UITableView and add images to each cell and then rotate the tableview so that it will look like an horizontal scroll.
The advantage of doing this method is that the UITableView will handle all the reusability issue and we dont need to worry about. But I am not sure whether its the right way to do this.
Btw.. I have uploaded an app with the UITableView rotated horizontal scroll view to the appstore without getting rejected ;)
It is not an good way to do that ,u can upload your photo on server side and when open the app. loading the image from server.
id path = #"http://upload.wikimedia.org/wikipedia/commons/c/c7/Sholay-Main_Male_Cast.jpg";
NSURL *url = [NSURL URLWithString:path];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [[UIImage alloc] initWithData:data];
imageView.image = img;
LooK at this answer
I have a gallery view of photos that are downloaded from the internet so I used Enormego's EGOImageView. I noticed that when i scrolled down my tableview after the images were in the cache, the scrolling would lag. I immediately found that when the image was retrieved from the hard drive with return [UIImage imageWithContentsOfFile:cachePathForKey(key)]; it was working on the main thread so I added the operation to an NSOperationQueue. This reduced the lag by half but the scrolling still stuttered. After going through the code, I noticed that in the success method
- (void)imageLoaderDidLoad:(NSNotification*)notification {
if(![[[notification userInfo] objectForKey:#"imageURL"] isEqual:self.imageURL]) return;
UIImage* anImage = [[notification userInfo] objectForKey:#"image"];
self.image = anImage;
[self setNeedsDisplay];
if([self.delegate respondsToSelector:#selector(imageViewLoadedImage:)]) {
[self.delegate imageViewLoadedImage:self];
}
}
commenting out the self.image = anImage; got rid of the lag completely (but obviously I get no image). And as far as I can tell, if I want to alter the UI, it must be done in the main thread. Is there a way to set the image for the EGOImageView without it lagging the scrolling?
Note: the JPGs are around 50kB
Thanks
Subclass uitableViewCell and do your own drowing in drawContentView.
Resize images in background thread and then present them in cells.
P.S: if the code found on git hub is not good enough for you, try to write your own that suits your problem.
From what I understand, all the steps required to initiate an asynchronous disk query takes a couple milliseconds on the mainthread, and that is enough time for the scrolling to look like it stutters, so I decided to completely remove hard drive caching for large images and instead create an NSMutableDictionary which holds the UIImage as an object, and the NSURL.absoluteString as the key. This works seamlessly but obviously has the disadvantage of being a memory hog. I checked out the memory usage of some photo-sharing apps and I've been able to get the memory usage for the app to over 100MB so it seems everybody else is doing this.
I have an array of UIImages that contains some .jpg images downloaded from the net when the app starts. I also have a table view that shows some of these images in its cells. The problem is that when I scroll the table, the app's memory consumption always increases up to the point where the app crashes due to low memory conditions. The table cells seem to be reused fine, so my theory is the following.
Since the UIImageView in a table cell only retains one of the elements in the image array, when the cell gets reused, and a new image is assigned to it, the previously used image is of course not destructed (the cell releases it, but the array still retains). However, the decompression cache used to hold the raw image data (computed the first time the UIImage is displayed by a view) belongs to the UIImage itself, so it also remains. But I'm just guessing about all this.
Can this really be the problem? If so, how can I work around it? My first idea was to create a copy of the UIImage whenever it is assigned to a cell, but looks like UIImages can't be deep copied. Is there any way to tell a UIImage to keep the compressed jpg data in memory but throw away the decompression cache? (In the worst case I guess I can still download all the images and store them in local files, then load them from there and completely release the UIImages when not displayed anymore, but this doesn't seem to be an elegant solution.)
--
I can't post the original code (as suggested in comments) as it is fairly complicated with custom table cells and custom views and even a background thread downloading the images, but I've just created a small test app and it seems to show the same behavior. Here's a little function that is called when the user taps a UIButton:
- (IBAction)onNext:(UIButton*)sender
{
static NSMutableArray* images = nil;
if (!images)
{
NSArray* names = [NSArray arrayWithObjects:
#"IMG_2957.JPG",
#"IMG_2962.JPG",
#"IMG_2965.JPG",
#"IMG_2970.JPG",
#"IMG_2971.JPG",
#"IMG_2978.JPG",
nil];
images = [NSMutableArray new];
for (int i = 0; i < names.count; ++i)
[images addObject:[UIImage imageNamed:[names objectAtIndex:i]]];
int i = 42;
}
static int current = 0;
imageView.image = [images objectAtIndex:current];
++current;
}
Yes, I know that the images array is leaking, that's not the point (in the original code I also want to retain the images for the entire lifetime of the app, and only release on quitting). But. According to Memory Monitor, after the first tap (all images get loaded, and the first one is displayed), the app consumes ~5MB (all jpgs loaded, one of them decompressed). After all subsequent taps, the memory consumption increases by ~2MBs (yep, I tested with 1024x768 images). So it looks like decompressed data of the previously displayed images is still there. I know this wouldn't be a problem if I released the image once it isn't visible anymore, and that would also release the decompressed buffer, but if possible, I'd like to retain all the images, and only release the decompressed data. Is that possible?
If you're reusing cells, make sure those cells implement:
-(void)prepareForReuse
{
// releasing the image
self.imageView = nil; // Or release depending on you memory strategy
self.labelView = nil; // get them all
}
I don't think UIImage has no memory leaks that fatal. I have been working on few applications which has to deallocate UIImage a lot due to memory constraint, but it's still working fine. I believe your image leaks somewhere. Show us some code so someone will point that out for you.
I need a clarification from all of you,That is I am implementing an iPad application. In that I tried to download and animate the images. The image count should be more than 100,000.The code I used to download and adding to the view is as follows.
UIImageView* imageView=[[UIImageView alloc]initWithFrame:CGRectMake(0, 0,100,100)];
NSData *receivedData=nil;
receivedData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://path/prudently/iphone/image_s/e545afbf-4e3e-442e-92f9-a7891fc3ea9f/test.png"]];
imageView.image = [[UIImage alloc] initWithData:receivedData] ;
[subView addSubview:imageView];
[imageView release];
But I am getting exception after I successfully added more than 8000 image to my subview. I am getting exception at getting data from the url. And one more thing I am not releasing the subview because once I downloaded them I need to animate the subview.
Please give me your suggessions
Thank you,
Sekhar Bethalam.
100,000 images would seem a lot for desktop applications, let alone a smart phone like an iPhone. Is there not another approach you can take to solve this problem that wouldn't need such a high resource count?
You can write the URL , images or something to a cached file, and divide some pages to animate the images...
When the user press a page link , application read and animate the images of this page, images of the page which user don't use need not display.
The only way you are going to accomplish this is to dynamically create and destroy the UIImageViews as they are needed on the screen. The iPhone/iPad/iAnything are incapable of doing what you want because of the limited memory available on the device.
I have a tableView with some large images in it. I'm struggling to improve the very jerky scrolling performance. If I use ImageNamed to load the images, scrolling is jerky at first, but after the images are viewed, scrolling is smooth. I know ImageNamed adds the images into the system cache, so my question is: is it possible to pre-load the images into the system cache before they are viewed?
I've tried by adding the following code to my viewDidLoad method:
for (int i = 0; i < appDelegate.detailSectionsDelegateDict.count; i++) {
NSString *imageString = [NSString stringWithFormat:#"%#",[[appDelegate.detailSectionsDelegateDict objectAtIndex:i] objectForKey:#"MainTrackImage"]];
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
UIImage* theImage;
theImage = [UIImage imageNamed:imageString];
[imageCacheArray insertObject:theImage atIndex:i];
}
I then draw the correct image from the imageCacheArray in my CellForRowAtIndexPath method. But the result is still jerky scrolling.
Thanks!
Getting a table view with images (especially large ones) to scroll smoothly is not as trivial as you might think. Loading up a bunch of images with [UIImage imageNamed:] will very quickly cause springboard to kill your app as it starts to exceed memory capacity. Take a look at the Core Animation session videos from this year's WWDC, specifically look at session 425, "Core Animation in Practice, Part 2" They cover this exact topic and it's very well done. You can also get the relevant source code if you sign in with your developer account.