0

in my app I need to display several images that I fetch from Parse and everything was great until I tested it on iPad mini and iPad 2...

To give you a better idea, my Home page is a tableview with 4 cells. The first cell must display a "big" image (50-55KB), the second cell shows a table view of thumbnail images(5-8KB) - but no more than 5 collection view cells! - and the other 2 cells are used for text, buttons etc.

I'm using Parse as a backend and I started with its tools to work with images like PFImageView: this way to load an image from the server I was using these two methods:

theImageView.file = thePFObject["<PFFileEntity>"] as? PFFile
theImageView..loadInBackground()

Once the app finished to load the home page , used memory device was at 100-110Mb, really close to 125-130Mb at witch the app is closed by the system.

I replaced the lines above with:

let imageData = NSData(contentsOfURL: NSURL(string: (stringContainingTheURL))!)
dispatch_async(dispatch_get_main_queue()) {
self.recipeImageView.image = UIImage(data: imageData!) }

and the memory usage is now at 83-85Mb. Better but as I go into another view containing only an YTPlayerView (for embedding youTube video) and a collection view of 5 thumbnail images, it easily shut down.

My strategies to reduce memory impact are: thumbnails images where possible; avoid UIImage (named:) method; avoid to work with PFFiles: this way then I store PFObjects into array, I work with strings instead of files; call didReciveMemoryWarnings() where possible to release memory

I've checked that there aren't memory leaks but I don't know how to improve the behavior. Any idea?

I'll leve you my code for the first two cells in order to compare with you.

class HomeTableViewController: UITableViewController, UICollectionViewDelegate, UICollectionViewDataSource {

//MARK: -outlets

var collectionView: UICollectionView! { didSet {collectionView.reloadData() } }

// MARK: - Model
let indexPathforcell0 = NSIndexPath(forRow: 0, inSection: 0)
let indexPathforcell1 = NSIndexPath(forRow: 1, inSection: 0)

var firstCellObject:  PFObject? { didSet{ tableView.reloadRowsAtIndexPaths([indexPathforcell0], withRowAnimation: .Fade) } }
var secondCellArrayOfObjects = [PFObject]() { didSet { tableView.reloadRowsAtIndexPaths([indexPathforcell1], withRowAnimation: .Fade) } }

// MARK: - life cycle

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

override func viewDidLoad() {

    super.viewDidLoad()
    //fetch
    fetchFirstCellObject()
    fetchSecondCellArrayOfObjects()
}

 private func fetchFirstCellObject() {
//At the end...
// self.firstCellObject = something
}

private func fetchSecondCellArrayOfObjects() {
//self.secondCellArrayOfObjects = something

}

// MARK: - Table view data source

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 4
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    var mainCell = UITableViewCell()
    switch indexPath.row {

    case 0:
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell1", forIndexPath: indexPath) as! CustomTableViewCell
        cell.object = firstCellObject
        mainCell = cell

    case 1:
        var cell = self.tableView.dequeueReusableCellWithIdentifier("Cell2") as! UITableViewCell
        collectionView = cell.viewWithTag(1) as! UICollectionView
        collectionView.delegate = self
        collectionView.dataSource = self
        mainCell = cell


    return mainCell
}

// MARK: - UICollectionViewDataSource

func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
    return 1
}

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 5
}

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    var cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! CommentedRecipeCollectionViewCell

    cell.object = secondCellArrayOfObjects[indexPath.row]

    return cell
}
}

And here how I implement the UITableViewCell:

class CustomTableViewCell: UITableViewCell {

// MARK: - Model

var object:  PFObject? { didSet { updateUI() } }

@IBOutlet weak var objectImageView: PFImageView!
@IBOutlet weak var objectLabel: UILabel!


private func updateUI() {
    let qos = Int(QOS_CLASS_USER_INITIATED.value)
    dispatch_async(dispatch_get_global_queue(qos, 0)) {
        let URLToUse = // ....
        if let stringToUse = theString {
            let imageData = NSData(contentsOfURL: NSURL(string: (URLToUse))!)
            dispatch_async(dispatch_get_main_queue()) {
                self.homeImageView.image = UIImage(data: imageData!)
            }
         }
    }

    objectLabel.text = // ....
}
}
Alekxs
  • 85
  • 7
  • Regarding the size of the image, don't look at size of the file in persistent storage (because that is compressed and is not representative of the amount of RAM that will be needed). Look at the dimensions of the image and calculate the memory usage from that (e.g. it often turns out to be width * height * 4, regardless of the size of the file). If the image dimensions are larger than what are needed by the image view, then you should [resize the image](http://stackoverflow.com/a/28513086/1271826) down accordingly. – Rob Aug 24 '15 at 14:41
  • Just a note Alekxs, in practice you will surely need to use DLimageLoader to properly cache images. Fortunately it is absolutely easy, example .. http://stackoverflow.com/a/19115912/294884 (sorry that's an old-days obj-c example) – Fattie Aug 24 '15 at 14:48
  • @Rob Thank you for your help, really! I red something about it but I skipped thinking it wasn't my situation. As I'm using Facebook images (my own ones) they are 960x541 - about 2Mb, am I correct? And thank you for the link, I'll investigate for sure! – Alekxs Aug 24 '15 at 14:56
  • @JoeBlow I was thinking to use https://github.com/rs/SDWebImage if it's the same because it's on Swift :-) Parse has it's own cache methods but I prefer to use other solutions. Thank you for the kind reply, I'll use all the suggestions! – Alekxs Aug 24 '15 at 15:07
  • Yep, that one image is going to take up 2mb or more. When using table/collection views, if you don't resize images appropriately, your memory consumption can really start to climb quickly. If you're still having problems, the next step is to use Allocations tool in Instruments can be useful for tracking down memory issues. See WWDC 2013 video [Fixing Memory Issues](https://developer.apple.com/videos/wwdc/2013/?id=410) for examples on using Instruments. – Rob Aug 24 '15 at 15:08
  • @Rob please, let me take advantage of your experience to ask you this: I've just implemented your extension and I see little spikes on memory usage before the image appears on screen. Could it be because of the heavy task? In that case, it seams to me like counterproductive . I'll definitely check the wwdc video as I need to practice with Instruments. Again, thank you for taking time helping people like me – Alekxs Aug 24 '15 at 15:34
  • @Alekxs Yep, you'll see the spike in memory when you first do the image resizing. You can't get around that (assuming you can't control the size of the original images), but you can make it less frequent if you cache the resized image somewhere (either in `NSCache` or even save a copy in the `NSSearchPathDirectory.CachesDirectory` if you want to cache it across sessions). So when you need to populate the image view, see if you already have a resized image in your cache, and if so use that, if not get image, resize it, and save resized rendition in case as well as updating image view. – Rob Aug 24 '15 at 15:39
  • Hi Alekxs .. regarding the **cache issue**. I can absolutely assure you that, **very unfortunately, Parse's image cache systems are unfortunately just not good enough for production apps**. Every new Parse release I carefully test them and they are no-go. IMO DLImageLoader is really spectacular. It's better than SDWebImage: far easier to use, and more solid. (BUT sdwebimage is OK.) I'm afraid I'm not up-to-date on whether DLImageLoader has a swift .. email the guy. – Fattie Aug 24 '15 at 17:12
  • Also Alekxs. Be aware that programming **long scrolls of images** is, basically **an incredibly difficult engineering challenge**. Just one of many issues, general discussion: http://stackoverflow.com/a/26034382/294884 It's just one of those annoying things that "nobody tells you". There's any number of examples of what you would call "Hello, scroll" code examples of how easy!! it is to make extremely long (don't even mention infinitely-loading) scrolling lists. But for actual production apps these examples are stupid and don't touch the various hassles you face. – Fattie Aug 24 '15 at 17:17
  • I'm not offering any instant solution (there is none) but just warning you that you have to be a Rob-class programmer (or perhaps have Him on hand to continually answer questions on these damned QA sites!) to really work through that sort of thing. It's worth noting that at like Facebook they have sort of **whole buildings full of engineers**, who do nothing other than, specifically, make the amazingly-well-working infinite, huge, long, image-and-video-packed scrolls (that we so take for granted in all the feed apps) work properly ... it's tough. – Fattie Aug 24 '15 at 17:19
  • Don't even mention the variable-height-cell issue (which, I believe but I'm not up to date) Apple have somewhat improved, made easier, more built-in just lately. – Fattie Aug 24 '15 at 17:21
  • @JoeBlow Rob , belive me that I'm not magnifying if I say that I'm extremely grateful to you guys! As you deduced, I'm "developing for fun" and when someone is alone and without the right skills, everything become really hard... I'm making a cooking app and at beginning I thought it was just a game - what a goofy! What Joe sad, about the Parse's image cache, cost me a day of workaround and I'm happy to receive a corroboration about my supposition. I'll definitely go with DLImageLoader at this point ;-) – Alekxs Aug 24 '15 at 20:14
  • Just to remain on topic, I hadn't the time to implement the cache but now both the home screen (which is showing a big image and last commented recipe images) and the "recipe book" tab (which is a tableview containing a collection view in every cell. Selecting the cell, will segue to a tableview for a list of recipes) are fine! Memory usage is always under 100Mb, like 84-96Mb, also for long table view images. I'm happy with it and for what I can do it's a decent result. – Alekxs Aug 24 '15 at 20:15
  • Now I need to fix a strange behavior in the most "empty" view of the entire app containing only a YTPlayerView for embedding youTube video and another collection view. When I open it, the app stops working ... Let me fix it and the next recipe, I'll cook it for you ;-) PS: with height-cell-issue, if you mean to resize every cell based on their content, now it takes two lines of code. BUT... if you are me.. it takes days to discover that sometimes, you need to adjust top/bottom constraints to fix auto layout conflicts. But this is another story! – Alekxs Aug 24 '15 at 20:15
  • Again thank you for all your important suggestions and for the time they took you to share with me. – Alekxs Aug 24 '15 at 20:16
  • go ahead and ask other specific questions, on this site, as you see fit, hope it helps – Fattie Aug 24 '15 at 20:33

0 Answers0