Memory issue when using large UIImage array causes crashing (swift)

I've run into low-memory problems myself in my own apps which have to work with a number of high resolution UIImage objects.

The solution is to save thumbnails of your images (which take a lot less memory) in your imageArray and then display those. If the user really needs to see the full resolution image, you could allow them to click through on the image and then reload & display the full size UIImage from the camera roll.

Here's some code that allows you to create thumbnails:

// image here is your original image
let size = CGSizeApplyAffineTransform(image.size, CGAffineTransformMakeScale(0.5, 0.5))
let hasAlpha = false
let scale: CGFloat = 0.0 // Automatically use scale factor of main screen

UIGraphicsBeginImageContextWithOptions(size, !hasAlpha, scale)
image.drawInRect(CGRect(origin: CGPointZero, size: size))

let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
imageArray.append(scaledImage)

And more information about these techniques can be found in this NSHipster article.

Swift 4 -

// image here is your original image
let size = image.size.applying(CGAffineTransform(scaleX: 0.5, y: 0.5))
let hasAlpha = false
let scale: CGFloat = 0.0 // Automatically use scale factor of main screen

UIGraphicsBeginImageContextWithOptions(size, !hasAlpha, scale)
image.draw(in: CGRect(origin: .zero, size: size))

let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

The best practice is to keep the imageArray short. The array should only be used to cache the images that are in the current scroll range (and the ones that are about to show for better user experience). You should keep the rest in CoreData and load them dynamically during scroll. Otherwise, the app will eventually crash even with the use of thumbnail.


Let me start with easy answer: You should not implement stuff that has been experienced by thousands of people by yourself. There are some great libraries that take care of that problem by itself, by implementing disk cache, memory cache, buffers.. Basically everything you will ever need, and more.

Two libraries that I can recommend to you are following:

  • Haneke
  • SDWebImage

Both of them are great so it is really matter of preference (I like Haneke better), but they allow you to download images on different threads, be it from Web or from your bundle, or from file system. They also have extensions for UIImageView which allows you to use 1-line function to load all images easily and while you load those images, they care about loading.

Cache

For your specific problem you can use cache that uses those methods to deal with the problem, like this (from documentation):

[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey];

Now when you have it in this cache, you can retrieve it easily

SDImageCache *imageCache = [[SDImageCache alloc] initWithNamespace:@"myNamespace"];
[imageCache queryDiskCacheForKey:myCacheKey done:^(UIImage *image) {
    // image is not nil if image was found
}];

All the memory handling and balancing is done by library itself, so you don't have to worry about anything. You can optionally combine it with resizing methods to store smaller images if those are huge, but that is up to you.

Hope it helps!