Goals:
Programmatically load first image (approx 94kb) into an ImageView in the main storyboard from the Assets.xcassets folder (see code below) - works perfect
Then when you load a second image (same size) into the original UIImage it causes the iOS app to crash.
Here is my code:
mainImageView.image = UIImage(named:"FirstImage.png") // load first image, no issues
Then if you programmatically load a second image into the same UIImage it causes the device to throw a low memory warning and the iOS crashes the app:
mainImageView.image = UIImage(named:"SecondImage.png") // load second image
After reading a number of answers on SO and other articles (see below), the best way to manage memory when you are loading multiple images into an animation array is to use the contentsOfFile: imageName instead of the UIImage(named:"FirstImage.png")
See Article here:
http://www.alexcurylo.com/2009/01/13/imagenamed-is-evil/
And secondly Apple states the following:
If a matching image object is not
already in the cache, this method locates and loads the image data
from disk or from an available asset catalog, and then returns the
resulting object. The system may purge cached image data at any time
to free up memory. Purging occurs only for images that are in the
cache but are not currently being used. In iOS 9 and later, this
method is thread safe. Special Considerations 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.
https://developer.apple.com/documentation/uikit/uiimage/1624146-init
Lastly, upon receiving the Memory Warning you could also create the following function:
func applicationDidReceiveMemoryWarning(application: UIApplication) {
NSURLCache.sharedURLCache().removeAllCachedResponses()
}
Hope this helps someone else :)
Related
When I try to render a GIF image (selected from photo library, whose data type is PHAsset), I use the following code:
PHImageManager().requestImageData(for: asset, options: nil) { (data, _, _, _) in
if let imageData = data {
imageView.image = UIImage.gif(data: imageData)
}
}
Where .gif is an extension to UIImage I copied from here, I believe many people use it.
The problem is, when I run the above code, the memory usage rises by about 20+MB, which is not outrageous, however, when I delete this selected GIF asset, the memory usage does not drop. And if I go ahead and select more GIF assets, for each one I select and run the above code, the memory usage rises by 20+MB. Now it's not acceptable anymore as the memory usage will just go up and never drop until the app crashes.
I understand why the memory usage goes up when I render a GIF image, I mean, the image data sits in the memory. What I don't know is how to I deallocate the chunk of memory when I wish to delete the GIF image?
--------------UPDATE-----------------
The UIImageView on "TestScreen" displays the thumbnail of the selected GIF image
When I press the GIF image, the app will open the image in full screen mode and if it's a GIF image, it'll play the animated image by running the above code
The memory goes up and never drops when I repeatedly open the GIF image in full screen
The memory leak may be in your own code rather than in the .gif extension. Maybe the view controller that displays the .gif does not deinitiazile when you close it. Wherever the leak is, here are two ways to find it:
a) A very simple approach is to add a print command to the de-/initialization of your objects. So you can see in the console when an object should be deinitialized and free up memory but in fact doesn't, e.g.:
class MyClass {
init() {
print("init: \(self)")
}
deinit {
print("deinit: \(self)")
}
}
b) A more insightful and convenient method is to use Xcode Instruments.
It is a much more powerful way of inspecting the memory management of your app than adding print commands. Also, once you have figured out how to use it, you will love it as it automates a lot of steps and eventually you will be able to check for memory leaks with just a few clicks.
Here you can find a tutorial on how to use Xcode Instruments.
If you post the code for the screen with the black background that opens and displays the GIF it may give a hint what the problem might be. In most cases it is something like a delegate that is not declared weak or another form of circular strong reference.
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.
Apples documentation says:
Because image objects are immutable, you cannot change their properties after creation. Most image properties are set automatically using metadata in the accompanying image file or image data. The immutable nature of image objects also means that they are safe to create and use from any thread.
Link
Also if you look at the answers of this "Thread safety of UIImage" question, it is concluded that it is safe to use them from any thread (at least since iOS 9).
Yet, there are comments that complain about issues, especially about creating UIImages on a background thread.
In my case I'm sure that this leads an issue where animations stop working.
Does anybody have insights on this?
I can't see your code but I suspect this is a UIImage vs UIImageView issue if you're experiencing animation problems.
A UIImage manages image data off screen.
A UIImageView displays your image on to your user interface.
Handling image data off the main thread with UIImage is fine.
Displaying or animating an image with UIImageView is not.
we are having problems with image not showing up on the apple watch, we tried the following :
Our image is located in the apple watchkit extension, and we tried to put it in the cache:
let img = UIImage(named:"slider_r.png")
WKInterfaceDevice().addCachedImage(img!, name:"slider_r.png")
It crashed immediately...
Then we tried to add the image directly on the apple watchkit app: no assets.
No image loaded up...
Then we tried putting it in an image.assets didn't work either...
We are currently out of ideas...
Thanks !
Alright, I've been working with WatchKit for about a month now - here's what I know:
Placing your image into the Watch App's Assets Catalog will add it to the Watch's bundle. This will be immediately available for use using UIImage's .setImageNamed("name").
Placing your image into the Watch Extension's Assets Catalog will add it to the extension's bundle, residing on the phone. Now, to get that to the watch you have to first grab a reference to the image from the extension, then add it to the device's cache, using WKInterfaceDevice().addCachedImage(image, name:).
Check out the Apple documentation for providing images to the Watch here. Here's the documentation for setImage(_ image: UIImage?):
This method changes the image being displayed. Use this method to send images from your WatchKit extension to the WatchKit app. The image interface object is resized to accommodate the size of the newly specified image. If the image itself is too large for the device’s screen, the image is clipped.
This is not what you want. You want to explicitly add the image to the cache so you can use it later. If you use setImage as described above, it will transfer the image from the phone to the watch every time you call it, which is not ideal if you're using the same image in multiple locations or you're using it very frequently. You have to be careful in choosing exactly which images you want to cache on the watch.
Using what we now know:
var img: UIImage = UIImage(named: "slider_r") // grabs the image from extension's bundle
WKInterfaceDevice().addCachedImage(img!, named: "slider_r") // adds to the Watch cache
myImageOutlet.setImageNamed("slider_r") // grabs the image from the Watch cache
Ensure that the asset catalog is included in the extension's target so that the images are correctly placed in the bundle at runtime.
You do not need to include the extension (e.g. ".png") in the image's name.
I'm really struggling with running out of memory in my app. The are the basic operations that I'm performing, as specified below. Note that whenever a user takes a picture with the camera, I save two different versions of it--one is a preview (for fast loading), and one with high quality for zooming and marking up later.
Here is a list of my order of operations:
At app launch, show thumbnails of images that have been taken in the app (obviously, at first launch of the app, this list is empty).
Touch camera button to launch the UIImagePickerController
User takes a photo and saves it (again, a preview is saved and a full scale photo is saved)
The list of thumbnails refreshes and shows the new preview image. Again, the overall size of "Image IO" in the VM Tracker instrument is still very small (increased by maybe 0.5MB tops).
User touches one of the preview images, which launches a different view controller that does the following:
Create new UIImageView, load the image, add as a subview to View Controllers view
Suddenly, "Image IO" jumps by 30MB.
The wouldn't be a big deal, but when I go back (navigation controller pop), neither the "Image IO" type nor the "Dirty" type decreases in size. It's as if the UIImageView is not being destroyed. Every time I push this view controller and load an image, the size increases by 30MB, and popping never decreases it.
I'm using ARC, so I'm not calling release myself, but I would expect that it would be destroyed when I pop the view controller.
Just FYI, the settings I'm using for saving the preview and the full image are:
Preview: [UIImageJPEGRepresentation([UIImage imageWithImage:image scaledToSize:CGSizeMake(300, 400)], 0.4f) writeToFile:previewPath atomically:NO];
Full: [UIImageJPEGRepresentation(image , 0.8f) writeToFile:imagePath atomically:NO];
My main concern is (1) figuring out how to get rid of the memory used from the UIImage after I pop the containing view controller, but I'd also like to know (2) why the image is 30MB when loaded.
i'm worked on image editor app and this help me lot for reduce memory use.Try this
don't load images with [UIImage imageNamed:] for load image in ImageView,it's cause memory issue when no.of images are more,
use this,
NSString * imgpath= [ [ NSBundle mainBundle] pathForResource:#"imageName" ofType:#"png"];
ImageView.image=[UIImage imageWithContentsOfFile: imgpath];
As suggested by #Matic Oblak...
(1) I have already seen similar problem with image view, try setting its image to "nil" before popping the controller and see if the problem persists.
RESULT: I explicitly set the UIImage AND the UIImageView to nil (not sure if both are needed), and sure enough, it released the memory.