Large amounts of memory allocated on setImage: - ios

I have a UIImageView that was created programmatically ([[UIImageView alloc] init]). The app's memory stays in check until the setImage: method is called. Any thoughts?

I'm assuming you're setting your image to your image view using something like this:
[imgV setImage:[UIImage imageNamed:#"yourImg.png"]]
The problem with using that is that the app caches these images. If you'd like to avoid caching images, use imageWithContentsOfFile::
[imgV setImage:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"yourImg.png" ofType:nil]]];
Also, be sure to set your image to nil when you're done using it:
[imgV setImage:nil];
I have had issues with this in the past, and here's some text from an email I got back from Apple in response to a TSI:
There are quite a few cases where you’re using the API UIImage
+imageNamed: to load images but you should be aware that imageNamed caches its image data even after the returned UIImage object is
released. Replacing calls to imageNamed with -imageWithContentsOfFile:
as outlined below is a way to ensure full control over your app’s
image data in memory

Related

How to cache CollectionViewCell images that is part of custom UITableViewCell

So, I have this news feed structure. Every news block is a custom UITableViewCell. Now, every custom cell has a CollectionView that shows images.
The problem is that when scrolling, and news cell (news block) comes out visible, the CollectionView is reloading - every time cell shows up. I'm trying to find the way to cache those images on the main ViewController side.
What would be the best approach?
If you want to avoid implementing your own cache/disk/memory handlers for this kind of job I strongly recommend either AFNetworking or SDWebImage that handle it all for you.
An example of SDWebImage on how to set an image and its cache:
[imageView sd_setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
This handles all the cache/disk/memory for you automatically.
Here is example of how you handle cache with SDWebImage:
// Set memory size limit (check with older devices to avoid memory errors when setting it to high value.)
[[SDImageCache sharedImageCache] setMaxMemoryCost:****];
// Clear disk cache
[[SDImageCache sharedImageCache] clearDiskOnCompletion:^{
}];
// Clear memory cache
[[SDImageCache sharedImageCache] clearMemory];
I would stay away from NSUserDefaults or the above mentioned example setting the images to dictionary or arrays to avoid memory errors and app crashes/performance.
You have to cache your UIImage instance, so, if you get some pictures from any source, you might write something like below:
/* some code before */
UIImage *yourImage = /* Get your UIImage from any available way */
[self.cache setObject:yourImage forKey:#"SomeUniqueID"];
/* some code after */
I guess, this code will look greater as a function, which obtains UIImage instance and it's id as parameters.
For my opinion, it's the most common way to store images. If you need a more proficient way without any overheads with third-party frameworks, you can read the documentation about NSCache.
But in general, you have a lot of different ways how to store your images:
Get the image from the disk
Get the image from the memory
Use one of the variants above, but apart them still use some lightweight & flexible frameworks, you can find a lot of on the GitHub.
So, the code I wrote above will just store images into the memory cache.

Getting launch image from XCAssets using imageWithContentsOfFile:

I'm attempting to load images from my LaunchImage image set in a .xcassets file, but I don't want do use imageNamed:, as it wastes memory and the image only needs to be displayed for a couple of seconds during initial launch.
I have tried multiple approaches to this, but so far I have only been able to load them using imageNamed:.
This works:
[UIImage imageNamed:#"LaunchImage-700-568h.png"]
This doesn't work (returns null):
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"LaunchImage-700-568h" ofType:#"png"]]
Is there any way to do this without adding the resources explicitly to the target (aside from the assets file)? Thanks!
Looks like the answer to your question is no.
According to XCAssets documentation.
Each set in an asset catalog has a name. You can use that name to programmatically load any individual image contained in the set. To load an image, call the platform specific class method, passing in the name of the set that contains the image. The OS will load the image from the set that is most appropriate for the current scale factor. The platform method for iOS is imageNamed:. For OS X the platform method is imageNamed:
So we must use [UIImage imageNamed:] method to load images from XCAssets catalog on iOS.
See similar question and answers here.

Memory utilized never goes down using UIImage:imageWithContentsOfFile

On my app i have several UIViewControllers to load multiple images on each one. Looking at the memory utilized i realized that the memory never goes down, as i present viewcontrollers and dimiss them, the memory always goes up. I started to search about and find out that the problem was the way i load images, the method i used was imageNamed:
UIImage *oneImage = [UIImage imageNamed:imageName];
So i changed the method to:
UIImage *oneImage = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:imageName ofType:#"png"]];
But the memory stills the same way...
I am using ARC and before the dismiss of the uivicontroller i call:
oneImage = nil;
And the problem persist, any idea of how to solve this?
The image probably goes inside the autorelease pool. I never understood this clearly about ARC and autorelease pools. I thought they became useless in ARC, useful only if you are mixing with old code, but it's not. Sometimes ARC release the resources even if you are using a method that returns an autorelease object right out of the scope and sometimes not. Use:
[[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:imageName ofType:#"png"]];
There is an interesting article about this topic from Matt Galloway.

Strange behaviour with iOS UIImageView and App going into background

I have a method, and in this method, there is this conditional:
if (self.sleepingCharacter.objectSprite.image == [UIImage imageNamed:#"sleepingRight.png"])
{....
This normally works fine. But I've noticed, in the iOS simulator, that when I put my App in the background by pressing command-H, and then i bring my app back, this conditional no longer works. Do you know why this would happen?
I tested to see if the code would work if I wrote this:
if (self.sleepingCharacter.objectSprite.image)
{....
And it did work, which means that there is still an image there. Now I am confused.
You are using the == operator to compare the two images. This will only be true of the two images are actually the same hunk of memory (the same pointer).
The UIImage imageNamed: method caches images. So in theory if you call it again and again for the same image name, you will keep getting the same pointer and your code appears to work.
But the image cache can get purged at times due to memory usage. Once the image gets purged, the next call to imageNamed: will return a new image pointer and your check will fail.
You need a better way to see if the two images are the same. One solution is to convert both images to NSData objects using UIImagePNGRepresentation then compare the two NSData objects using the isEqual: method.

UIImage imageNamed memory leak

I am trying to understand why I have memory leaks in a very basic implementation of a UIImage and a UIImageView.
I am not using ARC in that case (correctly disabled).
My code is pretty straightforward :
UIImage *image = [UIImage imageNamed:#"my_image.jpg"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[[self view] addSubview:imageView];
[imageView release];
I am implementing this code in the viewDidLoad: method of a UIViewController.
By calling the method imageNamed: of UIImage, I know that I will get an object that I do not own / an autorelease object. This object will also be retained by the UIImageView object instantiated just after. So the only object I have the ownership is the UIImageView one.
After running this app with the Memory Leaks Instruments, I have this report :
I heard about the cache system that operates but I should not have have memory leaks because some datas are cached.
Here is a reference to the answer with the cache explanation :
https://stackoverflow.com/a/2930567/1154501
Thank you in advance !
Edit : Tried also with ARC, and I got the same issue.
[UIImage imageNamed:] is managed by the operating system. Deallocating the UIImage created from this method might free up the memory that was allocated. If you have lots of images or user generated content, you should be using [UIImage imageWithContentsOfFile:] or [UIImage imageWithData:].
If you create too many images with [UIImage imageNamed:], your app may get killed by iOS because of memory usage. I made a sample app to prove this to myself, see more here: iOS UIImage storage formats, memory usage and encoding / decoding
Did you try to open the 'Extended Detail' right panel and look for the exact line where the memory is leaking?
Your code is OK, my opinion is that the leak is somewhere else.

Resources