Gif consumes lot of memory in iOS that leads app to crash with OOM : Swift

Varun Tomar
4 min readAug 6, 2021

--

Gifs are very popular type of media used to convey something upfront. Our team which works in development for media house have many reasons to use Gifs in our articles thumbnail and NEWS detail. Earlier we were not able to figure out that our apps consume hell lot of memory to load gifs but when content team started to make use of Gifs heavily, we eventually started to get crashes on prod. We were clue less as there were no major changes deployed.

We tried to trace logs of an affected user device and his journey in the app. Fortunately got a hint from a news article detail where we had 19 heavy gifs in a row. It blowed my mind 🤯 to see how memory consumption grows exponentially from 150 MB to 2 GB and boom 💥. Please refer below video to feel memory heat.

Here we were pretty sure that this memory spike is due to multiple Gifs. Now question is how to approach from here? We had two issue one is memory spike that lead OOM, second is jerk in our feed when we scroll. We tried following solutions to fix it :

  1. We were using SDWebImage library for all image related tasks like image downloading, rendering and cache, as a first step we went through it’s documentation to check if they have something specific to Gifs but unfortunately there was nothing that can cool down memory volcano 🌋.
  2. We opted down-sampling approach for images, this approach solved the crash issue and control memory spike but … but it converts Gif to a static image and shows only first frame of GIF😌🥲.
  3. We also tried other libraries like Kingfisher but didn’t help😌😌
  4. Also used UIGraphicsRenderer to render images, result is not desirable. It stoped memory spike but converts GIF to static image. (UIGraphicsRenderer Extension)
  5. What next ?— browsed on stack-overflow but we couldn’t find anything relevant.
  6. I thought we should do ✍️ something our self to crack this — — And here we go :

We started with ImageIO framework of iOS, which is responsible for all meta data info, pixels, colours and all, explored that thoroughly. The framework provides CGImageSource object for decoding and CGImageDestination for encoding images. ImageIO framework explain how an image could load incrementally, there is a beautiful concept of Interlacing and this is our life saver. We have to write a custom image decoder for us as UIImage doesn’t support animated images but UIKit do for sure. WebKit became our inspiration to crack it as they already did something to support heavy GIFs.

WebKit powers Safari on iOS and Mac, also browsers on other platforms like: Blackberry and Amazon Kindle.

WebKit is an open source, here is the source code. Here is the C++ file of our interest ImageDecoderCG.cpp.

Now we are starting with custom ImageDecoder class. Initialisation and setting data as follows :

ImageDecoder may need multiple frames for GIFs, so adding one variable to get total frames.

We need to get properties of an individual frame, for this we use CGImageSourceCopyPropertiesAtIndex function as below to get time interval of a frame :

We also need to calculate size of frames as follows :

You may notice options we pass to Image I/O functions. DBImageDecoder uses two sets of options. The first one asks an image to be cached in a decoded form. The second set used to decode an image asynchronously. Images are decoded immediately and from the full pixel size.

Also we need to specify down sampling level for Image I/O to perform downsampling as below :

We also need to give flexible decoding options as per need as below :

Now lets create image using frames :

We can check the image status as below :

😳😳 It’s too long, can we move to the climax how to use this ??

Be patient, yes we are almost there😀😀. Lets create an extension of DBImageDecoder to support UIKit elements. Crux is below two files are final snippets to make use of custom decoder.

Keep above two files handy, once you have image data with you, we just need to setData on DBImageDecoder object. Usage of DBImageDecoder as below : 👇

When you incrementally loading an image, create the image data object for accumulating the data. Pass the partial data toDBImageDecoder and the complete data when loading completes. You can use URLSessionDelegate for this as below :

Let me show you the result of it now 🧐🧐 Please refer below video :

We are loading 19 heavy gifs in a raw and consume around 200 MB, earlier it was getting crash as memory consumption shoots to 2 GB on the same article.

Is this what you are looking for? If yes then enjoy 😉

Hope you enjoyed this article. Thanks for reading this🙏🙏. Any feedback in the comment section would be appreciated. If you feel , please share and add some claps 👏 👏 .

Acknowledging AMIT for helping in this implementation

You can follow me for fresh articles.

--

--