Rendering GIF images causes extensive memory usage in Swift? - ios

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.

Related

iOS app crashes when loading an image

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 :)

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.

Add images in NSAttributedString and save to Core Data peformance

I'm encountering this huge performance issue while saving the NSAttrbutedString with images in it to Core Data.
There is a UITextView where allows the user to input text as well as adding images. When user finishes typing and click 'done' button, it will be saved to CoreData, and shows in a TableView.
Here is how I save the content when click 'done' button:
Create a private MOC and assign the AppDelegates managedObjectContext as its parent MOC.
privateMOC = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
privateMOC.parent = managedObjectContext
privateMOC.perform {
do {
try self.privateMOC.save()
self.managedObjectContext.performAndWait {
do {
try self.managedObjectContext.save()
} catch {
fatalError("Failure to save context: \(error)")
}
}
} catch {
print("Could not save \(error)")
}
}
There are two issues:
Depends on the images, when I click 'done' button, the UI is blocked and takes 3 - 5 seconds before the view dismissed and shown in the TableView.
I check the actual database .sqlite and found, one single newly-added entry(text and images in UITextView) makes the database size increases almost 12MB! (Maybe the images take too much space?)
Any suggestions how to tackle these issues?
Thanks!
These issues are going to be difficult to prevent in your use case. By using NSCoding on an NSAttributedString that includes images, what you're asking for is a binary blob where you can't control the encoding process in any way.
It might be that the image size is inflated, e.g. because you're saving the uncompressed data instead of a PNG or JPEG. But you can't do anything about that because you can't control how NSAttributedString handles it. So you're probably stuck with the large data size.
The long processing time is probably connected to the previous point-- you can't control what NSAttributedString is doing internally, but it's likely that a lot of that time is taken up dealing with the image(s).
You might improve on the save time if you encoded the attributed strings on your own instead of using a transformable attribute. Then you could start the encoding in advance, in the background, instead of having it happen when you're saving data. You probably can't fix the encoding time, but you may be able to make it happen earlier and be less noticeable. I think you're stuck with the size as long as you're using encoded attributed strings, though.
I don't know what your app is doing but if you could get away from potentially massive NSAttributedStrings you'll avoid the problems you're having.

ios: how to fix memory warning issue when showing lots of thumbnails(over 3000) by using NSCache?

I write a table view/collection view to show lots of photo.
I use dispatch queue to load photo from Internet and use NSCache to cache photo (for not loading photo from internet again).
when I loaded over 2000 photo, app received memory warning.
I tried to release the content of nscache in didreceiveMemoryWarning(), or set the nscache delegate to let nscache to auto release nscache content.
after releasing the content of nscache and I scrolled to the top of the table view,
sometimes: the thumbnail load different images several times (show
image1, image5, image 6)
sometimes: the thumbnail won't show anything
I guess what caused this happened is --> using dispatch queue to show thumbnails in table view
but I don't know how to fix it!!
anyone knows?
or is there any better way to show lots of photos(over 3000)?
thank you!!

UIImageView not releasing memory (according to VM Tracker) - Low Memory Warnings & Terminate

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.

Resources