Search code examples
iosswiftuitableviewuicollectionviewkingfisher

Message from debugger: Terminated due to memory issue While scrolling in UITableView/UICollectionView with gif images Kingfisher Library


Code snippet:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell: BroadCastFeedTableViewCell = broadCastTableView.dequeueReusableCell(withIdentifier: "broadCastFeedCell") as BroadCastFeedTableViewCell

    cell.singlePicImageView.kf.setImage(with: URL(string: (self.broadCastArray[indexPath.row].images_url?[0])!), placeholder: nil, options: nil, progressBlock: nil) 
            { (theImage, error, cache, url) in
                if theImage != nil{
                    let imageHeight = self.getAspectRatioOfDownloadedImages(resolution: self.broadCastArray[indexPath.row].image_resolution![0])
                    cell.collectionContentViewHeightConstraint.constant = imageHeight

                    cell.layoutSubviews()
                    cell.layoutIfNeeded()

                }else {
                    let placeHolderCenterCordi = UIView().getViewRelationWithSuperSuperView(viewControllerView: cell.collectionContentView, 
                                                                    subView: cell.singlePicImageView, subObjFrame: cell.singlePicImageView.frame)

                    cell.singlePicImageView.addPlaceHolderImageView(cordinates: placeHolderCenterCordi.center)
                }
               self.broadCastTableView.rowHeight = UITableViewAutomaticDimension
            }
}

In above code I have used Kingfisher library to load image from remote server, whenever following code load certain images(GIF, JPEG, PNG) which may have large size (2-5 MB approx.) the app terminates due to memory issue. In iPhone 5s it terminates instantly as soon as app is launched and in other iPhone Model (7, 6s) it terminates after scrolling for certain amount of time. I have also check Allocation and leak but I didn't understand/found much about the issue.

I have also attached the profiling graph. This shows there is no such memory leaks, but due to some issue app is terminating:

Following Image show their is no such memory leaks, but due to some issue app is terminating


Solution

  • I had a lot of problems with memory loading images. I cannot be certain of your case, but I can give you some helpful tips.

    1. Look into UITableViewDataSourcePrefetching. It's a way for you to download the images before they are rendered in the cell.
    2. Look into creating a Helper function somewhere that starts the download and allows that download to be cancelled. When you quickly scroll down a tableview, for instance, every cell is going to start the download of the image, even if it was not shown. You can cancel the download if the cell is gone.
    3. Cells should be basically a UI element on your code. The idea is, try to take out network requests out of the cell itself and make sure that you cell only contain the elements (images, labels, views) to render, the cell has a func configure(withModel: Model) function, and that on the cellForRow, you just pass the data to it for rendering. Cells are reused and updates frequently, you should not be performing a bunch of stuff on it. Remember that when a cell get loaded, it's going to call those functions, then it can be gone out of screen and back again and it's going to re-execute all that all over again.
    4. Have you cell class perform the correct layout updates and refreshes and try not to do it in the cellForRow. All you could do is just make sure the imageview gets the image, but not rely on performing cell changes and this image download is async and that cell may not even be there when the download ends.
    5. Caching. Can you cache those images or they change constantly? make sure you understand how KF is caching your images and that is doing the caching you need. If not, adapt.
    6. [nitpicking] you call cell.layoutSubviews() and then cell.layoutIfNeeded(). The first one is force calling layoutSubviews, the second one is checking to see if there are stuff that needs to be laid out before calling layoutSubView. If you want to mark a view to be laid out you can do setNeedsLayout(). that will make the view layout updates on the next cycle.