Setting imageView.image causes unbounded memory growth

Viewed 19

Steps

  1. Create UIImages in array
  2. Load UIImage into reusable collection view cell
  3. Scroll
  4. View memory XCode

enter image description here

Code that repros the issue

import UIKit

class Cell: UICollectionViewCell {
  static let ReuseIdentifier = "reuseID"
  var imageView: UIImageView

  override init(frame: CGRect) {
    imageView = UIImageView()
    super.init(frame: frame)
    addSubview(imageView)
  }
  
  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
  
  override func layoutSubviews() {
    imageView.frame = bounds
    super.layoutSubviews()
  }
  
  func configureWith(image: UIImage) {
    imageView.image = image
    layoutSubviews()
  }
}

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
  
  var images:[UIImage] = []
  var collectionView: UICollectionView
  
  required init?(coder: NSCoder) {
    let layout = UICollectionViewFlowLayout()
    layout.minimumLineSpacing = 0
    layout.minimumInteritemSpacing = 0
    
    collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
    collectionView.register(Cell.self, forCellWithReuseIdentifier: Cell.ReuseIdentifier)
    super.init(coder: coder)
  }
  
  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    
    view.addSubview(collectionView)
    collectionView.frame = view.frame
    collectionView.backgroundColor = .green
    collectionView.dataSource = self
    collectionView.delegate = self
    
    loadMoreImages()
    collectionView.reloadData()
  }
  
  func loadMoreImages() {
    for _ in 0...50 {
      images.append(cloneImage(image: UIImage(named: "bigImage")!))
    }
  }
  
  // MARK: - UICollectionViewDataSource
  func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    print(images.count)
    return images.count
  }
  
  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Cell.ReuseIdentifier, for: indexPath) as! Cell
    cell.configureWith(image: images[indexPath.row])
    return cell
  }
  
  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return view.frame.size
  }
  
}

func cloneImage(image:UIImage) -> UIImage {
  let newCgIm = image.cgImage!.copy()!
  let newImage = UIImage(cgImage: newCgIm, scale: image.scale, orientation: image.imageOrientation)
  return newImage
}

Video demo: https://i.stack.imgur.com/QRDWl.gif

Things that resolve the issue

  • Make UIImages optional, and set UIImages to nil after they've been displayed
  • Comment out imageView.image = image
  • Not using cloneImage, and instead images.append(UIImage(named: "bigImage")!)

Any idea what UIImageView is doing under the hood that causes unbounded memory growth? Seems like it's allocating a bunch of memory in the UIImage object that never gets released.

0 Answers
Related