UICollectionView received memory warning while scrolling - ios

I am developing an App with an UICollectionView in one ViewController.
This CollectionView it's a gallery with 3 images in each row.
I get the images from the server in groups of 30 pictures, first url and code, and when the cell is going to be displayed in cellForItemAtIndexPath I use SDWebImage Library to download those pictures asynchronously. Every 30 pictures I call again the web service and I get 30 pictures more sending the request with limit and offset. This could happen 100 times, there are profiles with 3000 pictures.
Well, my problem comes one I launch the app in my iPhone 4, after some fast scrolling I start getting Received Memory Warnings and after some warnings the app crashes. When I make the same test in the Simulator, nothing bad happens.
Each time I download 30 pictures I add the result array to the NSMutableArray *data property which handles the CollectionView data and reload the collectionView. I have tried to use Instruments with allocations, but it is very difficult for me to understand what is happening.
This is the code I use to create the cells
-(UICollectionViewCell *)collectionView:(LZProfileImagesCollectionView *)aCollectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if(aCollectionView == self.imagesCollectionView){
if([self.data count] - 12 == indexPath.row){
self.photoOffset += 30;
[self loadUserData];
}
LZProfileCollectionViewCell * cell = (LZProfileCollectionViewCell *)[imagesCollectionView dequeueReusableCellWithReuseIdentifier:ImageCollectionCellIdentifier forIndexPath:indexPath];
Image *image =(self.data)[indexPath.row];
[cell configureCellWithImage:image];
return cell;
}
return nil;
}
And in the LZProfileCollectionViewCell cell I have this one:
-(void)configureCellWithImage:(Image *)image
{
[self setNeedsDisplay];
// Imagen
NSString *imageUrl = [kBaseURL stringByAppendingString:image.imageStringUrl];
[self.pictureImg setImageWithURL:[NSURL URLWithString:imageUrl] placeholderImage:[UIImage imageNamed:kDefaultLoadingImage]];
[self.pictureImg setContentMode:UIViewContentModeScaleAspectFit];
}
I have taken setImageWithURL from SDWebImage
After 3 minutes I get this snapshot in the Instruments(Statistics)
Thank you in advance

Well, you are caching every image in an array, so once you cache so many images you are eventually going to run out of memory. It doesn't happen on the simulator because the simulator has as much memory as the computer running it, so it is unlikely to ever run out of memory, a real device however will run out very fast. I'd handle the memory error yourself and empty the array, to free up new space for new images. Or better yet create your own caching mechanism to hold and clear images that haven't been in view for a while, either way you have to have some way to dump images after you hit a certain threshold.

In the method – didReceiveMemoryWarning you should free some memory of the array where you are stored the images. Maybe the first images that you are not displaying stored it a second array like an auxiliar array.

This is probably happening because a lot of UIImage objects end up in memory. You could instead cache this on disk. AFNetworking has a nice UIImageView category that does all of this automatically.
If you're not saving the Images in your dataSource array, then they're probably being retained somewhere else, which is a bug.
Also, make sure you're dequeuing cells and not creating new ones.

I've experienced similar situation so I'm replying on this issue though the reason seems a little different from yours.
I have a horizontal UICollectionView inside UITableViewCell. When I scroll UICollectionView right and left rapidly, the entire UI(even a home button) stops and after a while the app crashes with the message "Message from debugger: Terminated due to memory issue" on the console even without didReceiveMemoryWarning call.
I tested it with a UICollectionViewCell without any UI components but no changes. XCode Profile tool says it's actually keep allocating memory during the UI's stopped until it crashes even without any UI components in it.
The only suspicious point was that I was setting the width of a UICollectionViewCell's subview(custom UIView pinned to all superview edges) with AppDelegate.window.size.width * 0.35 to determine UICollectionViewCell's width and it was a float value like 130.25
When I hardcode this value to 130, all the crashes are gone. I think something was happening under the hood with this vague value of 130.25 and it's determining the width as 130 or 130.25 occasionally.
So I'm using CGFloat(Int(AppDelegate.window.size.width * 0.35)) now and everything's fine.

Related

iOS Photo Gallery Memory Leak / Crash when using Push Segues

I've created a small photo gallery which is presenting a new view controller with a larger version of the photo and some additional text when it is clicked:
The problem is - after going through a handful of images - the application crashes due to overuse of memory. I attempted to resolve this by compressing the images in order to leave a smaller memory footprint, but the issue remains and I'm not sure what else I can do to resolve this issue.
Also - there is almost no code to doing this since I'm using storyboard's push segues as well as the built in navigation item to go back between viewControllers.
P.S.
If you feel source code is necessary to provide insight in this instance - it can be found here:
https://www.dropbox.com/s/q1qq8pq4tzv8wyo/EXAMPLE%20BUILD.zip?dl=0
To resolve this issue you have to use this trick; Put a "placeHolder" image in your cell's imageview in "StoryBoard". Don't load the images all at once in your "ViewController", load them one by one by running a loop or in your "cellForRowAtIndexPath()" method and add a delay in each iteration (Load first image then add a delay, load second image and add a delay, then for third one and so on up to the last image).
If you want to know how to add a delay then check this link:
NSTimer - how to delay in Swift
To resolve this issue I simply resized the images - I noticed I accidentally used a gigantic (6000 x 4000) image and even though I compressed the images iOS had to crunch pretty hard to resize them into the view... thereby causing the memory leak and subsequent crash.
Resizing them to 600x400 did the trick.

App crashes due to memory leak - Swift

I'm developing an iOS app that displays more than 1000 images -locally stored-, After using it for some time, app crashes due to memory leak.
I tried Profile tool, but couldn't figure out where the issue is, I'm displaying these images in both tableView and Icarousel. scrolling through app, memory increases but doesn't seem to be released.
Things I've already done:
Resizing Images(This just delayed the crash).
Clearing dictionaries and datasources- didn't make any difference-.
I tried Using "contentsOfFile" instead of "named" property when creating image- didn't make any difference also-.
Clearing the image in reusable func in the tableViewCell -nothing changed-.
Note:
I'm using AdMob, but even when I don't use it, memory still leaks.
As I understood you're storing your images in NSArray or something like that to represent them in table view.
Try to use NSCache to store your images in RAM. It will automatically clean used memory if it'll need.

UICollectionView & AVPlayer Performance

I have a UICollectionView whose cells each have an AVPlayerLayer, all playing a video at the same time. About 9 cells fit on the screen at a time and it's pretty laggy so I need ways to boost the performance to achieve smooth scroll. There's a few things I've tried already:
1) Each cell has only one instance of AVPlayer. It doesn't get created, instead, player.replaceCurrentItemWithPlayer is called when the video url changes.
2) Because I'm using ReactiveCocoa, it's trivial to skip repeat urls to avoid playing the same video twice.
What else can I do to speed up the scroll and performance?
First I want to say it's a bit crazy to see several players in action at the same time: it's a heavy task of rendering anyway.
As far as I know, simply scaling/re-framing the video to smaller size doesn't make it less content to render: if you have a 100x100 picture to render and you render it in a frame of 10x10, it still consumes the same amount of memory as the original picture would consume; same thing goes for videos. So, try to make your video assets having similar resolution as the frame where you would present them.
Store each cell in an NSCache or NSMapTable using the NSIndexPath as the key for each; then, whenever you need a cell, call one directly from the cache or map table.
Obviously, you'll have to create all the cells at once, but you'll get the same scrolling performance as you do with nothing in the cells at all.
Need sample code?
Here's my latest iteration of a perfectly smooth-scrolling collection view with real-time video previews (up to 16 at a time):
https://youtu.be/7QlaO7WxjGg
It even uses a cover flow custom layout and "reflection" view that mirrors the video preview perfectly. The source code is here:
http://www.mediafire.com/download/ivecygnlhqxwynr/VideoWallCollectionView.zip
Unfortunately, UIKit will hit bottlenecks and drop frames when put under pressure for this use case. If you can muster refactoring code to use Facebook's AsyncDisplayKit / then this would be your best bet.
For objective-c / this project here is a good reference.
https://github.com/TextureGroup/Texture/tree/master/examples/ASDKTube
- (ASCellNode *)tableNode:(ASTableNode *)tableNode nodeForRowAtIndexPath:(NSIndexPath *)indexPath
{
VideoModel *videoObject = [_videoFeedData objectAtIndex:indexPath.row];
VideoContentCell *cellNode = [[VideoContentCell alloc] initWithVideoObject:videoObject];
return cellNode;
}
there is swift code available if you dig deep enough in github.

How to load large amount of images in iOS?

I am developing an app which displays large number of images. when i'm loading images there's is no problem on loading of 1000 images when the image crosses the 1000 the app crashes and memory issues raised. How to solve it?
Is there any way. Kindly help me?
Thanks in Advance
Your app is crashing due to consuming too much memory. So you need a strategy to manage the memory your app uses.
You need to set up a pooling-type system where you only load those images that might be displayed "soon".
For example, if the user is looking at image 500, you could have 498, 499, 501, and 502 loaded into memory.
If they move to image 501, then you deallocate 498, and load 503.
What constitutes "soon" is dependent upon how your app's flow works, but you should be able to come up with something workable.
You need to come up with a tableview-like system.
If you look at them, all cells aren't loaded at the same time ; you can experience this if you load your app with a tableview, then put a breakpoint in cellforrow, and you'll see it being called when you start scrolling.
Because all cells aren't loaded at once.
You need a similar logic with your images ; you should NEVER load all of them at once. And if you have a reason to, then I strongly suggest loading a thumbnail version of them ( a really low quality version of them, for a grid-like view of all your pictures, for example ).
To achieve that you need to know what pictures need to be immidatly visible, and what pictures are likely to be visible soon. For small numbers let's say you have 10 pictures total (instead of 1000).
User is loading the app, and can see picture 1 and 2 on the screen. You'll obviously load them straight away, as well as picture 3 and most probably picture 4 (just to make sure). When he scrolls, you KNOW that he is scrolling and your need a method that reads that scroll to allocated the right pictures dynamically. You'll deallocate picture 1 first (because its the first to go away ) and start loading picture 5.
That way, instead of having 1000 pictures loaded, you have only up to 5 (depending on the size obviously).
A really good example is the 9gag app, which loads pictures right under the screen, which means by the time you read the first gag, the next one is already loaded. The "only" way to see it load is to scroll a bit faster or to have a poor connection.
This logic is, to me, the most reliable and effective way to load a big amount of data, wether it is pictures or anything else.
Using stuff like cache, arrays, scrollview(s) and maybe files (depending on your app really) is a good starting point.
Probably you're getting the image-date from some API, because it's ridiculous to save it into device.. :)
My solution is to work with UITableView or UICollectionView depends on what design flow you're are choosing, because those UIComponents will manage memory for you a.k.a will allocate the visible once + 5 down and + 5 up, and deallocate the others.
Also you will need to download the image-date asynchronically, so you
won't block the main UI - thread.
Now... if i were you, I'll definitely go for third party library such as AFNetworking in your case, its best for handling JSON data from server, also have very good error handling.
Here I'll post a method witch is for downloading image-data from server.
- (void)downloadImageAsynchronicallyByUrlString:(NSString *)url forIndexPath:(NSIndexPath *)indexPath {
// Perofrm request
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc]initWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]]];
[requestOperation setResponseSerializer:[AFImageResponseSerializer serializer]];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// IF SUCCSESS send dataSource SUCCSESS message
[dataSource downloadFinished:responseObject forIndexPath:indexPath];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// IF FAIL send dataSource FAIL message
[dataSource downloadFailAtIndexPath:indexPath];
}];
// Start request!!!
[requestOperation start];
}
Explanation:
You call every time this method in cellForRowAtIndexPath at your tableView or itemForRowAtIndexPath if you use collectionView and I'm using a protocol to handle when responseObject a.k.a theImage to send the responseObject via protocol method.
In your UIClass where your tableView pr collectionView lives just implement your protocol method
- (void)downloadFinished:(UIImage *)image forIndexPath:(NSIndexPath *)indexPath {
// Creating copy of cell
_lecturerCell = (LecturersCell *)[_lecturersTableView cellForRowAtIndexPath:indexPath];
// Set downloaded image
[_lecturerCell setLecturerImage:image];
}

UIImage being released but CGImage not being released

I appear to have a memory management issue when I create UIImage instances the CGImages are not being released.
I have a paging UIScrollView to scroll through a series of JPG images. Below is my entire class that is a page view in the paging scroll view.
The code is being run on the main thread. The code uses ARC. I have tried loading the images using imageWithContentsOfFile: (returns an autoreleased object) as well as initWithContentsOfFile:(returns a retained object). I have tried #autoreleasepool and using performSelectorOnMainThread to ensure that the code is being run on main thread as suggested in other posts.
When scrolling through the images memory usage just grows until the app gets terminated as illustrated in the screenshot from instruments. Note the high allocation to image io.
Screenshot showing virtual memory usage
In the following screenshot it can be seen that the GTGImageScrollerPageViews, UIImageViews and UIImages are being deallocated. Notice there are Transitory objects for these numbering in the high 300's. However the CGImages are not being released and the number of CGImages living is in the high 400's and 0 Transitory.
Screenshot showing allocations
EDIT: Previously I had been recycling and re-using GTGImageScrollerPageView instances in the ScrollView as is the common pattern for scrollviews like this. In order to simplify while trying to debug this problem I allow the entire GTGImageScrollerPageView to be deallocated after it has been displayed in the ScrollView. As you can see in the second image above, there are only 4 GTGImageScrollerPageView living and 377 transitory, there are also 388 UIImageViews and 389 UIIMages listed as transitory so it appears that the UIImageViews and UIImages are being deallocated fine.
If I manually release the CGImage with CGImageRelease (Commented out in the code below) the CGImages are released. I know that I should not do this because I do not own the CGImage but this is useful to verify that this is where the problem is occurring. The screenshots below show the same code tested in Instruments but with CGImageRelease uncommented.
Screenshot showing virtual memory usage with CGImageRelease used
Screenshot showing allocations with CGImageRelease used
In the profiling outputs where CGImageRelease is used, you can see that the correct number of CGImage objects are Living and Transitory and that memory does not grow unbounded. Furthermore the app does not crash during usage with CGImageRelease used.
If this were some system caching of CGImage then it should release the memory when a memory warning is raised, but it doesn't. Memory just continues to grow.
What here could be causing this unbounded growth in memory?
Here is the code for the page view
EDIT: In response to comments I have updated the code to simplify further thus eliminating distractions such as ivars and properties that are not needed to demonstrate the problem. The problem remains unchanged and the results are the same when profiling. I have also added in the NSLog which also outputs the thread. I do see the dealloc being called as expected on the GTGImageScrollerPageView and it is always thread #1 and the call to displayAspectThumbImage is always on thread #1.
I really dont believe that there is anything wrong with the code that is presented here and this is confirmed by the generous effort of Rob. Something else is causing this but all the code related to loading and displaying the image is here; there is no other code in effect here. The only other notable thing that I could think of raising is that the displayAspectThumbImage method is called from scrollViewDidScroll and scrollViewDidEndDecellerating methods of the scrollview delegate but the fact that the methods are called on the main thread should exclude autorelease issues due to being run on another thread.
I have checked and NSZombies is not enabled and there are no Zombies increasing my memory usage. Indeed when the CGImageRelease method is called the memory usage is pretty flat as can be seen in the screenshot above.
#implementation GTGImageScrollerPageView
- (void)dealloc {
NSLog(#"%s %#", __PRETTY_FUNCTION__, [NSThread currentThread]);
}
- (void)displayAspectThumbImage:(NSString *)path {
NSLog(#"%s %#", __PRETTY_FUNCTION__, [NSThread currentThread]);
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.bounds];
[self addSubview:imageView];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:path];
[imageView setImage:image];
//If uncommented CGImages are disposed correctly
//CGImageRelease([imageView.image CGImage]);
}
#end
Tested on:
iOS 6.1.4
iOS 5.0.1
Really sorry to be answering this question myself but thought it would be useful to share my experience.
It turned out to be a category on UIImage that was part of a third party library I was using. I wont name the library as I have tested with the latest version and the problem is not there so there is nothing to gain by naming the library.
It is particularly strange as the library was not being used anywhere near the code that was leaking. It was only being used in one place in the whole project, but any time I used a UIImage it seems that it was affecting the UIImage instance. The mere presence of this category in the project was enough. This came as a real surprise.
The way I solved this was to begin by simulating the scenario in a new project and found that the code was not leaking there. I then migrated the code a little at a time and when I moved the categories across the leak appeared.
Huge thanks to Rob for his generous efforts to help me with this and even though he did not provide the solution directly, talking to him was really helpful in solving the problem. Nice to know there are such cool people out there.
I did a simple infinite scroller using your code and after scrolling through nearly 100 images the memory usage was, as one would have expected, thoroughly uneventful:
Looking at your source, I would have recommended a few little things (e.g. I would have put aspectImageView into a private class extension rather than the .h, I assume you're setting pageIndex from the calling routine but I'd add it as a parameter of displayAspectThumbImage, I would have made aspectImageView a weak property and refactor the code accordingly (e.g. create image view, add it as subview, then set the weak imageview property to point to this object), etc.), but none of those have direct bearing on your issue here.
Bottom line, your problem does not rest in the code you've shared with us.
Try to use property instead of ivar.
#property (nonatomic, strong) UIImageView *aspectImageView;

Resources