3

I'm a new developer, and my app has a collection view (scrolling horizontally) with 32 images. I have created arrays for the images, labels, and text. As I get to the end of the list, the app crashes. Regardless of the size of my images, I am getting 'Message from debugger: Terminated due to memory issue'. I have utilized the Instruments tool, which seems to indicate the the images are not being released.

I tried to resize the images but with no successs. I also tried using autoreleasepool, which I may have used incorrectly.

My image array is set up like this with images 1-32.

let imageArray = [UIImage (named: "1"), UIImage (named: "2"), etc.]

Here is my collectionView:

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return self.imageArray.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MainCollectionViewCell", for: indexPath) as! MainCollectionViewCell

    cell.imgImage.image = imageArray [indexPath.row]
    cell.lblImageName.text = nameArray [indexPath.row]

    return cell
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

    let mainStoryboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
    let desVC = mainStoryboard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController


    desVC.image = imageArray[indexPath.row]!
    desVC.name = nameArray[indexPath.row]
    desVC.text = textArray[indexPath.row]

    self.navigationController?.pushViewController(desVC, animated: true)
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044

2 Answers2

4

A few observations:

  1. Don’t load images up front. Load them as they’re needed.

    So, rather than imageArray which an array of UIImage, save an array of image names, e.g.

    let imageNames = ["1", "2", ...]
    

    Then, in cellForItemAt, that’s where you can instantiate the UIImage for that particular cell, e.g.

    cell.imgImage.image = UIImage(named: imageNames[indexPath.row])
    
  2. Remember that the images use a lot of memory. Regardless of what the size of the JPG or PNG, when they’re used in the UI, they generally use 4 bytes per pixel. So consider a 2000×2000px image, whose JPG might only be 100kb: Even when used in a tiny 100×100 image view, the whole image is uncompressed and will, in this case, use up 16mb! So make sure your images have dimensions that are appropriate for your UI. E.g. if your image view is 100×100, then you might have three renditions of the image, one that is 100×100, for non-retina devices, one that is 200×200 for @2x retina devices, and one that is 300×300 for @3x devices.

  3. Regarding memory not getting released, three points:

    • Eliminate your use of the array of UIImage and that will avoid forcing the app to hold all images in memory even though they may not all be needed at any given moment in time (see point 1, above).

    • Make sure you don’t have a strong reference cycle. So dismiss the view controller in question and then use “Debug Memory Graph” and make sure the view controller was actually dismissed.

    • When you use UIImage(named:), the images will be cached and memory won’t be released until there is memory pressure (e.g. a memory warning). Usually UIImage(named:) cacheing behavior will work (when you fix the above issues), but if it’s not, use UIImage(contentsOfFile:) instead. As the UIImage(named:) documentation warns us:

      This method looks in the system caches for an image object with the specified name and returns the variant of that image that is best suited for the main screen. If a matching image object is not already in the cache, this method locates and loads the image data from disk or from an available asset catalog, and then returns the resulting object.

      The system may purge cached image data at any time to free up memory. Purging occurs only for images that are in the cache but are not currently being used ...

      If you have an image file that will only be displayed once and wish to ensure that it does not get added to the system’s cache, you should instead create your image using imageWithContentsOfFile:. This will keep your single-use image out of the system image cache, potentially improving the memory use characteristics of your app.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
3

Keep Image names in an Array and in the dataSource get image name from array and construct image.

let imageArray = ["1", "2"]

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MainCollectionViewCell", for: indexPath) as! MainCollectionViewCell

    cell.imgImage.image = UIImage (named: imageArray [indexPath.row])
    cell.lblImageName.text = nameArray [indexPath.row]

    return cell
}

Note: If you still face memory issue then you might be loading heavy resolution images, better resize them in background thread and assign to imageView on main Thread.

Muhammad Waqas Bhati
  • 2,775
  • 20
  • 25