Search code examples
iossdwebimagealamofireimagekingfisher

iOS image [Kingfisher || AlamofireImage] "Terminated due to memory issue"


So my app only displays images in a CollectionView and it crashes because of memory issues. This is the memory graph Memory Graph

This is the sample project you can check. ImageTest

I've tried this same project with Kingfisher Library and AlamofireImage Library and it crashes on both.


Solution

  • It seems likely that the problem is caused by your images being too large. I see two solutions.

    PINRemoteImage

    Try using PINRemoteImage. It's in ObjC but you can bridge to Swift. This framework allows you to set limits on cache size, which should prevent your memory from being gobbled up.

    However, this might not help because you could end up not having all your images.

    Scale images down

    Because, as you noted, scaling the images one by one is manual (and therefore tedious), I suggest scaling on the client-end.

    To do this, you would probably end up writing your own caching code. I've done this before though, and I can attest that it is actually quite simple to get something that meets your needs. For example, when I had to cache images, I ended up creating a dictionary that stores images with url keys. Before you store the images in the dictionary, you scale them down.

    As requested, here is some sample code to help you out. This isn't the entire code, but it is a very solid base.

    Downloading the image

    Use Alamofire to download the image from a URL:

    Alamofire.request(.GET, "https://robohash.org/123.png").response { (request, response, data, error) in
        self.myImageView.image = UIImage(data: data, scale:1)
    }
    

    Scaling the image

    Use the code from this answer on SO. You should scale to the size that you need the image and no more.

    Storing the image

    Let's back up a bit. I would have all of this code managed by a class, ImageManager, or something like that.

    ImageManager should have:

    var delegate: ImageManagerDelegate?               // the delegate; more detail below
    private(set) var images: [URL: UIImage] = [:]     // the cache
    
    func getImage(from url: URL)                      // the entry point to the class; calls the delegate immediately if the image is already cached, else calls `downloadImage(url: url)`
    private func downloadImage(url: URL)              // actually downloads the image; calls `cacheImage(url: url, image: downloadedImage)`
    private func cacheImage(url: URL, image: UIImage) // caches the image in `images` with `url` as the key, and notifies `delegate` that an image has been cached with the specified url.
    

    ImageManager should also implement ImageManagerDelegate:

    protocol ImageManagerDelegate {
        func imageManager(_ manager: ImageManager, didGet image: UIImage, from url: URL)
    }