UIImage memory

Does reloading new images into a UIImage view increase the memory usage each time a new image is loaded, thus potentially leading to a memory leak? If so, then do I need to release the memory used for the currently loaded image before reloading the UIImage with a new image?

Images called with named: may be kept in cache, hence using memory.


I found this interesting discussion on the matter:

h ttps://www.hackingwithswift.com/read/30/6/fixing-the-bugs-running-out-of-memory


There's probably a simpler way, by setting image to nil, as described here:

https://stackoverflow.com/questions/17666679/uiimageview-not-releasing-memory-according-to-vm-tracker-low-memory-warnings

>>There's probably a simpler way, by setting image to nil, as described here:

Does this mean I should set the UIImage "image" to nil right before loading any new image like the following?



Does the same memory issue occur when setting images on Buttons (instead of UIImages) as well with the following?


setImage(UIImage(named: myButtonImageFile), for: .normal)

>>There's probably a simpler way, by setting image to nil, as described here:

Does this mean I should set the UIImage "image" to nil right before loading any new image like the following?

Yes, that's what I understand (that should flush the cache). But I didn't test.

Did you test ? What is the result ?


Does the same memory issue occur when setting images on Buttons (instead of UIImages) as well with the following?

setImage(UIImage(named: myButtonImageFile), for: .normal)

I suppose so, because it is linked to UIImage(named:)

I will update my code to always set the image to NIL before setting a new image (see below) and let you know:


myButton.setImage(nil, for: .normal)
myButton.setImage(UIImage(named: myNewButtonImageFile), for: .normal)



In my card game application I am seeing memory constantly increase throughout the game. Since there is only 52 different card images which can be displayed throughout the game, I would expect the memory to "level-out" eventually once all various card images have been loaded one time. I was just concerned whether reloading the same card images over and over into Button or UIImage views, while playing multiple rounds of cards, would keep increasing the memory footprint. This is why I wondered if constantly loading new images into UIViews (and Buttons) would cause memory to keep increasing even if the image was loaded once before.

What could happen is that when you reload you create a new instance (another object), without freeing the previous one.


One way to check is to add a print(theUIImageView) for a specific object (card 0 for instance) and see if you alsways call the same or create new instances.and check

I toggled my "mute button", which switches images back and forth (mute/unmute/mute/etc.) and then used "print" to display the "mute_button" view object after each time the image was reloaded to the new image. The output is below. It seems the addresses stay the same


<print output not allowed to be displayed ... since "It is being moderated" for some reason>


I cannot figure out how to set an UIImage view to NIL before reloading .... any ideas?

What is the print statement ?

It should be print(myButton)


If it is different each time, to set to nil, I would

- have a property as

var buttonImage : UIImage?


then

buttonImage = nil
buttonImage = UIImage(named: myNewButtonImageFile)
myButton.setImage(buttonImage for: .normal)

1) With the code below, I see the following output print statements


print("mute_Button = \(String(describing: mute_Button))")
mute_Button.setImage(UIImage(named: muteOffImageFile), for: .normal)


print output not allowed to be displayed ... since It is "Currently Being Moderated" for some reason. Always happens .. UGH



2) To set a UIButton image to NIL before reloading I am doing the following:


mute_Button.setImage(nil, for: .normal) 
mute_Button.setImage(UIImage(named: muteOffImageFile), for: .normal)


3) To set a UIImage image to NIL before reloading I am doing the following: Is this correct?


imageObj.image = UIImage(contentsOfFile: "") 
imageObj.image = UIImage(named: imageFile)

I don't want to see the description, but the object instance itself:

priint(mute_Button)

The following print statement in my code displays text which I cut and pasted into this post. However, the post now states it is "currently being moderated" for some reason so I guess you cannot see it. I had to remove the print output so I could post this response


print("mute_Button = \(mute_Button)")

It is very frustrating that posts indicate "currently being moderated" when there is nothing visually wrong with the text I am including

I tried manually retying all the text output by hand by still "currently being moderated"


I have no idea how to show you the text output, sorry


Can you please let me know how you would set a UIImage (not UIButton) to NIL?

>>I don't want to see the description, but the object instance itself:

print(mute_Button)


This print statement displays a long hex value for the UIButton. This seems to be the actual address of the UIButton itself since this address does not change when I switch the button image. The print statement also prints out "frame, opaque, autoresize and layer " data as well.


It does not seem like the actual UIButton instance (address) is changing when I reload images.


Do you think UIImage image would act the same way?



To set a UIImage image to NIL before reloading I am doing the following: Is this correct?


imageObj.image = UIImage(contentsOfFile: "")
imageObj.image = UIImage(named: imageFile)

Try adding some spaces in the text that requires moderation ; it it is an URL, it is always moderated (except SO, apple URL) ; change to

h ttps or w ww

there is no url or www it is just normal text as far as I can see.


toggling the MUTE button in my application causes the following print statments to be displayed :


Finally figure out the issue was with the <number> ... see below


I cannot provide the 12-digit <number> below since it will be moderated if I include it


mute_Button = Optional(<UIButton <number> frame = (324 35; 35 35); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x603000132fd0>>)

mute_Button = Optional(<UIButton <number> frame = (324 35; 35 35); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x603000132fd0>>)

mute_Button = Optional(<UIButton <number> frame = (324 35; 35 35); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x603000132fd0>>)

mute_Button = Optional(<UIButton <number> frame = (324 35; 35 35); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x603000132fd0>>)

mute_Button = Optional(<UIButton <number> frame = (324 35; 35 35); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x603000132fd0>>)



<number> = 0 x 6 1 7 0 0 0 0 8 0 3 8 0

Setting the UIButton image to NIL and UIImage image to NIL before I reload it does not seem to resolve my memory issues. It may reduce the memory a bit, but I am not sure how to tell for certain.


mute_Button.setImage(nil, for: .normal)  
mute_Button.setImage(UIImage(named: muteOffImageFile), for: .normal)



imageObj.image = UIImage(contentsOfFile: "")  
imageObj.image = UIImage(named: imageFile)

I understand using "UIImage(named: ***)" to load an image onto a UIButton or UIImage will result in the image file being cached automatically. Since the total size of all 52 card images combined together have a small total size of 1/2MB only then I am assuming this automatic caching of images is not what is causing the didReceiveMemoryWarning( ) one of the players received while playing the game.


I was trying to determine whether anything in my application was wasting memory but really do not know how to go about figuring this out. I was thinking it might be due to loading images into UIButton and UIImage views, but know I do not know what to think.


Question:

If I have 6 UIImage views on my storyboard and eventually load 52 different card images onto each individual UIImage while playing many rounds of my game, then does each UIImage view cache 52 separate images individually (ie: 52 images x 6 UIImage views = 312 cached images) OR does the system only cache the total 52 card images only?

The documentation of UIImage just says that init(named:) uses cache, but it says nothing about its details.

So, just my guess, it is very likely that the name passed to init(named:) is used as a key for caching, so, if you have only 52 names, UIImage would cache 52 something in memory.

Though, a cached image in memory may be far bigger than the image file.


Another guess, only 52 card image would rarely cause memory issue and you may have something wrong in other parts of your code.

Have you checked memory leaks using Leak?


One more, as far as I tested, setting nil does not affect internal memory usage of iOS.

I will look into what Leak is ... thanks

You may have found it already, but you can find many articles searching with "xcode how to use leaks".

("Leaks" is the right name and it is included in the Instruments of Xcode.)


Anyway, pleae tell me anything you find that may be related to your issue.

This dictionary structure of cache would explain the need to nil the UIImageView as well, to flush the cache, and not individual images, that clear only one in 52.


That's what was hinted in initial link:

set the UIImage AND the UIImageView to nil (not sure if both are needed), and sure enough, it released the memory.

https://stackoverflow.com/questions/17666679/uiimageview-not-releasing-memory-according-to-vm-tracker-low-memory-warnings

Sorry, but I do not understand what you mean by this. Any parts of this thread, as well as the content of the link, do not refer dictionary structure.

And even if we assume some sort of dictionary structure, it does not explain anything. A simple dictionary-based cache holds strong reference to the entries. Something like UIImageView can have a strong reference to the image held in the cache, but two references are dependent and releasing one would not cause another to be released.


If you know some sort of cache implementation which flushes one entry if a reference to the entry somewhere else is released, would you please show me a sample implementation?


That's what was hinted in initial link:

Sorrry, but the accepted answer was written about 7 years ago. Have you confirmed that the same thing applies to the latest iOS and the latest Xcode?


And one more, the answer is suggesting that setting its image to "nil" before popping the controller, not before setting another image to the image view.


As I wrote, the details of caching feature is not documented well, something you have suggested in this thread might have effect. Could you please show some concrete example that your setting-nil do have effect. I would like to see a testable code.

No, I've no code to test, I was just trying to elaborate from your statement:

the name passed to init(named:) is used as a key for caching,


I may have been wrong in inferring that this key was for a dictionary (or something like) ; but the point was that remiving the 'data' for this key would not clear the complete cache with all keys.


Was I badly misanalyzing how all this could work ?

Was I badly misanalyzing how all this could work ?

Some parts of your logic may be wrong, and some assumptions or tests of mine may not be represeting the actual issue.


Maybe we need to wait for the fact which will lead us to solve the issue. I expect that OP finds something soon.


You're right, it was a bit risky from me to elaborate too much on assumptions. Facts based conclusion is just better (in code or in many other cases).

UIImage memory
 
 
Q