Swift Image Memory - ios

I am making an app where I am displaying a lot of images, for example a profile image for a player object. I have all the images stored in Media.xcassets, and the images are named after the name of the player object they belong to.
playerImage.image = UIImage(named: playerName)
I have found that after I've loaded an image, the memory usage goes up permanently, although the image is no longer displayed. It can for example go up 3/4 Mb, while the image file is just around 50kb. Even after I leave a viewController it seems the memory use from the displayed image is still there. (I am using unwind segues, so there isn't stacking of UITransitionViews)
(The increases in memory use come when I am displaying images that haven't been displayed before)
Eventually the app crashes when the memory reaches around 1GB. Is there any way to fix this, or reduce memory use?

I had the same problem just last week. What I did is to change all:
UIImage named
to:
UIImage imageWithContentsOfFile
The memory allocated with the first method seems not to be released by the Garbage collector, whereas the memory allocated with the second one yes.
Im not a swift master, but I think this will work:
UIImage(contentsOfFile: (Bundle.main .path(forResource: "imageName", ofType: "png"))!);
Hope this helps.

Related

How to clear memory after UIImageView.animationImages?

I've created a new Xcode project that has only the following:
code creating an array of UIImages from png's dragged in to the project (I have tried both UIImage(named:) and UIImage(contentsOfFile:) when appending images to the array)
a UIImageView
a button which sets the imageView.animationImages = arrayOfImages and calls imageView.startAnimating()
When the button is pressed and the animation plays, the memory usage increases by 150-200MB, depending on the number of images in the image array. Then the memory usage remains at that level.
Setting imageView.animationImages = nil doesn't clear the memory. How could I go about clearing that memory?
Any suggestions would be helpful. Thanks!
1. Don't implicitely cache images
I guess you are using UIImage(named:)?
This method caches the images, see:
https://developer.apple.com/documentation/uikit/uiimage/1624146-init
Apple recommends:
If you have an image file that will only be displayed once and wish to ensure that it does not get added to the system’s cache, you should instead create your image using imageWithContentsOfFile:. This will keep your single-use image out of the system image cache, potentially improving the memory use characteristics of your app.
So using 'UIImage(contentsOfFile: String)' should solve your problem. Due to the documentation you need to supply a path to the image, the image name is not sufficient, see here:
https://developer.apple.com/documentation/uikit/uiimage/1624123-imagewithcontentsoffile
There it is also mentioned:
This method does not cache the image object.
2. Don't hold references to the images
When you are loading the images into a local array make sure to empty it.
self.imageArray = []
3. Set imageView.animationImages to an empty array
self.imageArray = []
self.imageView.animationImages = self.imageArray
Quick verification of the allocations in Instruments:
As you can see the memory is reclaimed. All good.

PNG image never deallocated

Still pain with memory debugging.
I have 4 VC's I load by using navigation controller. Every VC has its own PNG images used for several controls.
In Instruments I realized that most of the VM regions are occupied by ImageIO_PNG_Data.
And as I push/pop VC's those VM increases and never decrease (I was supposing that dealloc some VC would also release images).
Of course, the debug is done in the Simulator.
To expand slightly on rokjarc's comment:
UIImage +imageNamed: explicitly caches. The documentation states:
This method looks in the system caches for an image object with the
specified name and returns that object if it exists. If a matching
image object is not already in the cache, this method loads the image
data from the specified file, caches it, and then returns the
resulting object.
So images loaded previously will remain in the cache unless or until the memory is needed elsewhere. There's no efficiency to be gained from freeing memory up needlessly.
If you want to avoid the caching for whatever reason — I would argue whatever spurious reason — you could use +imageWithContentsOfFile:, or the normal init equivalent, having obtained the full path from NSBundle.
PNGs set to image views and other places via the interface builder will be accessed via the cache as far as I'm aware.
If the VM allocations do not have physical memory allocated to them there is no problem.
iOS memory maps files and there may be no physical memory allocated at any given time. Some VM allocations are frameworks that are shared by other apps.
What you need to watch are Living Heap Allocations which in this case is a little over 4MB.

IOS 6.x How to use Sprite Sheet with initWithContentsOfFile, imageNamed

I have been searching these threads and other sites but have not come across a way to do this both efficiently and memory friendly. And so, here is my story:
My IOS (iPad) app uses sprite sheets (a large image, such as 2k x 2k at 16 bpp, which is composed of many smaller sprites). I have created a sprite atlas class which manages these sheets, handle sprite animations, and other features.
The idea (in the load method) is to load in the sheet from the file system, split it apart into UIImages (one per sprite) using CGImageCreateWithImageInRect, and then dispose of the loaded sheet. Seems simple enough.
Note that the sheet is loaded into a UIImage by initWithContentsOfFile or imageNamed (more on that below).
The desire is to save file memory by using sprite sheets, and then save runtime memory by only retaining the actual sprites themselves as UIImages. In my experiments thus far this is what I find happening:
If I use initWithContentsOfFile I see (from Instruments) that it appears to do a file open, fstat64, and close for the file for EACH sprite in it. This takes an horrendous amount of time to load all the sprites. It actually seems to load the entire sheet, grab one sprite, close the sheet, then load the entire sheet again for the next sprite, until they are all created. Also it appears to consume lots of memory (proven by the "received memory warning" after just the second sheet loaded, as well as by Instruments allocations).
Next I tried imageNamed (which caches the sheet). The file loading occurs once and so it is MUCH faster. All seems good and in fact and I can go until many sheets are loaded. But eventually the dreaded "received memory warning" appears... and a few seconds later the app crashes. It appears to be the case that the cached image (even though the pointer is set to null after pulling out the sprites) never goes away. I have read several posts that also state this seems to be its behavior (although other posts say other things, so that is not conclusive - does anyone know definitively?).
And so it appears that neither method is what is needed. What I want is to load the file, pull out each sprite into its own UIImage, then have the UIImage for the big sheet file released completely.
I have read one site that talked about using the initWithContentsOfFile approach to set up their own caching system (rather than trust the IOS plan with imageNamed) so they can release the image when desired. However, I don't think they had sprite sheets in mind.
And so, I turn this over to the experts out there to see if there are some ideas on how to get both fast load times AND use minimal memory.
[and yes, I know that IOS 7 has SpriteKit. But this needs to also work on IOS 6.x.]
One interesting data point is that on an original iPad the imageNamed version actually works fine with no "received memory warning". It might have been IOS 5.x. But the app will crash on the iPad 2 device.
I am not including code here because what I am after is an understanding of the mechanics involved with how memory is used with these functions related to image handling.
And while I am at it, can someone please clarify this point:
True or False: When using CGImageCreateWithImageInRect, what it does is actually creates from new memory a bitmap the size of the rectangle specified and then COPIES from the original UIImage the pixels into this new memory (as opposed to setting up the bitmap format and having a pointer point into the original UIImage's pixel data). I think this is True, but want verification.
Thanks!

save many UIImage to NSMutableArray without memory leak or similar method

I am trying to do screen capturing and saving many images into NSMutableArray. Then, I will call those images and make video. I can successfully make video. However, the problem is that I can't save many images into NSMutableArray. The application crash. I would like to know how to save those images temporarily. (not in document directory).
You will quickly run out of memory if you try to store all of those images in an array. You have two options:
Even though you say you don't want to save all of those images to the Documents folder, that might be your best approach. Save them to Documents, and then have your video creation process load the images one at a time and add them to the video.
Alternatively, you can do the renderInContext of your view that you want to capture directly to the CGContextRef that you've set up for your CVPixelBufferRef, do the appendPixelBuffer, and but then CVPixelBufferRelease immediately.
Either of these approaches will avoid holding all of the images in memory at any given time, mitigating the out of memory situation. I profiled both approaches, and each avoids the constantly growing consumption of memory that the loading of images to an array suffers.

UIImageView not releasing image data properly?

In its simplest form, my app displays 10 UIImageViews, each containing an image. Even with all UIImageViews containing images, my app uses a small enough memory footprint. However, there is a button to clear all the UIImageViews by setting all their images to nil. The problem is, when checking Memory Monitor in Instruments, the memory held by the UIImageViews is NOT going away. This doesn't appear in the Allocations instrument, confirming the remaining memory footprint is not an object, but instead graphics-based memory. If I resize the images to something smaller or larger, the memory remaining is also smaller or larger, respectively.
Why is the image data sticking around after the UIImageView's image has been set to nil?
I believe UIKit keeps a cache of images for reuse. UIImageView might be releasing the object, but a copy is kept around for performance reasons.
These images, though, should be released on receiving a memory warning. If they're not, there's two places I'd check:
Make sure the UIImageView is being dealloc'd. Use Allocations Instrument to profile your app and do whatever you need to do in the program to load those images. Then unload the images and do a search for UIImageView. As long as you're sure your program should have released all of them, if you find any in the search you know something is wrong.
I'd also check any places the image was created, for example: UIImage = [UIImage imageName:#"Foo.jpg"]; Make sure these are also being released. You can use allocations to find UIImage classes, but it'll be harder to weed out the ones that should/should not be there.
Run the static analyzer: In Xcode 4 it's under Products -> Analyze. This is a great tool for finding logic errors, over/under release (if you not using ARC) etc.
Until actual UIImageViews are themselves released, their memory will remain allocated. Additionally, if you're using convenience methods on UIImage to obtain your images, eg:
UIImage *myImage = [UIImage imageNamed:#"myImage"];
Note that your image may cached behind-the-scenes by iOS, and so even if the image is being released by you, the memory footprint may still reflect the presence of the image in memory (eventually iOS will release it, so this shouldn't adversely impact your resource consumption).

Resources