Memory issues with UIImage animation image caching
I've been banging my head about this for a few days now and I just can't figure it out. Basically I have a keyboard extension that uses a UICollectionView to display animated UIImages. When it works and I switch to another app and then switch back to the keyboard, I get a splash of around 10MB. This obviously causes the application to crash after multiple swaps.
I've gone through the tools and it looks like I'm getting a bunch of extra UIImages when the keyboard reboots they "end up" clearing up, but it takes time and the cumulative effect maximizes memory. I think it has something to do with UIImage caching the animation image when I start at about 150-200 UIImages, which roughly matches all the animation frames I have in all cells. This pretty much doubles every reboot.
I threw this into the prepareForReuse cell:
override func prepareForReuse() {
if imageView?.isAnimating() == true {
NSLog("Stop Animating")
imageView?.stopAnimating()
}
imageView?.animationImages = nil
contentView.subviews.map({$0.removeFromSuperview()})
imageRect = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height)
imageView = UIImageView(frame: imageRect!)
type = nil
super.prepareForReuse()
imageView?.contentMode = UIViewContentMode.ScaleAspectFit
contentView.addSubview(imageView!)
}
But to no avail. It wasn't even triggered by a reboot. So, new cells are being created, but the old ones are not being cleaned up properly, perhaps?
I tried to run this:
func clearCellImages(){
for cell in collectionView.visibleCells(){
cell.imageView!?.animationImages = nil
cell.imageView!!.image = nil
}
}
In viewDidDisappear and deinit, but no difference.
It took me a long time to find this issue and now that I have it, I am stumped. Happy to post any code you think will help, very happy to be pointed in the right direction.
Thank.
source to share
So it turned out to be a two-way solution. First, I made these little helper functions:
func clearCells(){
for cell in collectionView.visibleCells(){
clearCell(cell as! MyCellView)
}
}
func clearCell(cell:MyCellView){
if let iv = cell.imageView {
iv.animationImages = nil
iv.image = nil
iv.removeFromSuperview()
}
}
I called clearCells
on viewWillDisappear
and saw a drop in UIImages
and memory usage, however if I scrolled through, which is more than likely, the images that were used in the cells that were currently not visible were not canceled, resulting in a small, but not an insignificant memory creep. I fixed this by calling clearCell
in the delegate method didEndDisplayingCell
:
func collectionView(collectionView: UICollectionView, didEndDisplayingCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath) {
clearCell(cell as! MyCellView)
}
I would really think that such control could be handled, if not automatically with ARC, or with UICollectionView
, and then at least manually through prepareCellForReuse
, but apparently not.
Hope this helps someone :)
source to share