0

I'm developing a Comic Reader App, and by now, I'm close to having a MVP, but I'm facing an issue which I've been struggling for weeks and I can't solve.

I've tried a lot of different things, but none of them seem to resolve the issue.

I'll put you in context:

When the user taps on a comic, the App pushes a ViewController that has a UICollectionView that displays each page of the comic, and the user can swipe between pages.

If the user taps on this UICollectionView, two toolbars appear:

  • Top Toolbar: A toolbar displaying the name of the comic, and a button to exit the ViewController

  • Bottom Toolbar: A toolbar displaying a UICollectionView that displays all of the pages that there are on the comic:

enter image description here

Well, this particular UICollectionView, is consuming a LOT of RAM Memory and I don't know why.

I'm using KingFisher and the code to render every UIImage inside the UICollectionViewCells is as follows:

  • I create the cell like this in the UICollectionView:

      func cellForPage(at indexPath: IndexPath) -> UICollectionViewCell {
      if let cell = mainCV.dequeueReusableCell(withReuseIdentifier: kSmallPageCVC, for: indexPath) as? SmallPageCVC {
          let page = self.pages[indexPath.row]
          cell.configure(page: page)
          return cell
      } else {
          return UICollectionViewCell()
      }
      }
    
    
      func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
      return cellForPage(at: indexPath)
      }
    
  • Then, in the "cell.configure" method, I set the image using the URL I passed by parameter:

      func configure(page: URL) {
          self.page.kf.setImage(with: page, placeholder: nil, options: nil, progressBlock: nil) { [weak self] (result: Result<RetrieveImageResult, KingfisherError>) in
              switch result {
                  case .failure(let error):
                      print(error)
                  case .success(let imageResult):
                      self?.page.image = imageResult.image
              }
            }
          }
    

Well, this takes a LOT of RAM memory, up to 1GB of RAM (if the comic size is big), to the point that the App crashes. Why is it taking so much RAM memory, even if I use the "prepareForReuse" method, setting the image to nil?

Is there any way to optimize the memory allocation to be able to render the images without taking so much RAM memory?

I find it very ridiculous for it to use so much memory.

All of the Apps like Whatsapp, Twitter, Instagram, Facebook, Telegram, even other Comic Reader Apps, show several images with full resolution without crashing the App.

Could someone give me a hint here please?

I heard about the autoreleasepool method, but I don't think it is appropiate to use it, and I think it is a far more optimum way to display those images without allocating so much memory. So please, avoid suggesting that on the comments. Or, if you suggest it, explain to me why are you suggesting this, and why is the best option in order for me to use it.

I used native Swift too in order to set the images, using UIImage(contentsOf: myUrl), but the results are the same, plus, the UICollectionView freezes when I scroll. So please, avoid suggesting that too on the comments.

Best regards,

Thanks.

Gerard Riera
  • 183
  • 1
  • 11
  • I think the image size is big. you have to load the thumb image instead of the original image. – Raja Kishan May 03 '21 at 16:39
  • @RajaKishan Yeah, each image size is like 2Mb, but I thought that the allocation clears each time I use the "prepareForReuse" method. – Gerard Riera May 03 '21 at 16:40
  • but still, it will create an issue. try to add some compression techniqe. – Raja Kishan May 03 '21 at 16:47
  • @RajaKishan could you suggest to me any compression technique, please? It would be much appreciated. Thanks. – Gerard Riera May 03 '21 at 16:52
  • Instead of downloading your images to memory (dataTask) just save them on disk using a downloadTask. You can also create thumbnails of your images and cache them using NSCache – Leo Dabus May 03 '21 at 16:53
  • I think kingfisher already provide but you can check this https://nshipster.com/image-resizing/ – Raja Kishan May 03 '21 at 16:54
  • Compression you can use jpeg. You can also use HEIC but this depending the device. Some of them don’t support HEIC but AFAIK most devices nowadays do support HEIC – Leo Dabus May 03 '21 at 16:55
  • @LeoDabus I already have them on the disk. When the user imports a comic, I have a library that extracts the images on the disk, so I'm loading the images directly from the disk, using kingfisher. – Gerard Riera May 03 '21 at 16:55
  • Why would you use an async method for loading local resources? – Leo Dabus May 03 '21 at 16:56
  • @LeoDabus because, even though the images are on the disk, it takes time to load them, so if I use native Swift, the UICollectionView freezes a little bit in order to load the image, but if I use Kingfisher, the swipe is smoother. – Gerard Riera May 03 '21 at 16:58
  • 1
    @GerardRiera Well if they are stored locally on disk it shouldn’t take long to load them. The flash storage is pretty fast. Again for the thumbnails don’t use an async method just NSCache and load it regularly from the disk. – Leo Dabus May 03 '21 at 17:00
  • @LeoDabus Yeah, but as I said in the description, if I use, for example "UIImage(contentsOfFile: url)", it takes the same amount of RAM as if I load it asynchronously using Kingfisher. Why is it taking so much RAM if I already have the images on the disk? I never used NSCache, I'll try it, thanks. – Gerard Riera May 03 '21 at 17:40
  • 1
    @GerardRiera Again you should create thumbnails of your images. No need to cache the large images https://stackoverflow.com/questions/29137488/how-do-i-resize-the-uiimage-to-reduce-upload-image-size/29138120#29138120 – Leo Dabus May 03 '21 at 17:41
  • @LeoDabus understood, I'll try that. Thanks. – Gerard Riera May 03 '21 at 17:43
  • @LeoDabus something like this you say? self.page.image = UIImage(contentsOfFile: page.path)?.resized(withPercentage: 0.5) – Gerard Riera May 03 '21 at 17:59
  • 1
    No first resize your original image. Second cache it. Third load it from memory (NSCache) – Leo Dabus May 03 '21 at 18:01
  • @LeoDabus understood, thank you very much. I'll let you know when I'm done. – Gerard Riera May 03 '21 at 18:02
  • @LeoDabus for the moment, I've tried the "resized" method, and it drastically decreases the RAM usage. Thank you very much. – Gerard Riera May 03 '21 at 18:49
  • 1
    Greetings @LeoDabus, now I'm doing the following: Every time I load a cell, I load the image from cache if available. If not available, I store the resized image into the cache, and show the resized image. It works so much better now. No freezing, all the uicollectionviews slide like butter, and barely no RAM consuming. The comic that consumed approximately 1Gb of RAM, now, with this method, is consuming between 25Mb to 35Mb of RAM tops. Thank you very much for your help. – Gerard Riera May 04 '21 at 09:06

0 Answers0