如何在使用UIImage时清除缓存以释放内存 - Swift 3.0

5

在阅读了一些SO和其他文章(见下文)之后,当您将多个图像加载到动画数组中时,最好的内存管理方法是什么?

http://www.alexcurylo.com/2009/01/13/imagenamed-is-evil/

这是我的目标:

  1. 创建动画数组(见下面的代码)
  2. 在动画播放后清除缓存并加载另一个动画(反复执行,你懂的 :)

正如苹果公司所述https://forums.developer.apple.com/thread/17888

使用UIImage(named: imageName)会缓存图像,但在我的情况下,在连续播放2-3个动画后,iOS终止了应用程序(操作系统没有响应低内存警告,而是终止了应用程序-请参见下面的代码结尾)

我不想缓存图像,我们可以选择以下两种方式之一:

  1. 每次动画完成后清除内存,然后加载新的动画;或者
  2. 当用户移动到新场景时清除内存

这是我的代码:

// create the Animation Array for each animation

func createImageArray(total: Int, imagePrefix: String) -> [UIImage]{
    var imageArray: [UIImage] = []
    for imageCount in 0..<total {
        let imageName = "\(imagePrefix)-\(imageCount).png"
        let image = UIImage(named: imageName)! // here is where we need to address memory and not cache the images

        //let image = UIImage(contentsOfFile: imageName)! // maybe this?

        imageArray.append(image)
    }
    return imageArray
}


// here we set the animate function values

func animate(imageView: UIImageView, images: [UIImage]){
    imageView.animationImages = images
    imageView.animationDuration = 1.5
    imageView.animationRepeatCount = 1
    imageView.startAnimating()
}

// Here we call the animate function

animation1 = createImageArray(total: 28, imagePrefix: "ImageSet1")
animation2 = createImageArray(total: 53, imagePrefix: "ImageSet2")
animation3 = createImageArray(total: 25, imagePrefix: "ImageSet3")

func showAnimation() {
    UIView.animate(withDuration: 1, animations: {
        animate(imageView: self.animationView, images: self.animation1)

        }, completion: { (true) in

        //self.animationView.image = nil // Maybe we try the following?
        //self.animationView.removeFromSuperview()
        //self.animationView = nil

        })
    }

根据 Stack Overflow 的回答,这似乎是防止图像被缓存的最佳方法,但在我的代码中似乎不起作用:
let image = UIImage(contentsOfFile: imageName)!

我也尝试过这个方法,但似乎也不起作用:
func applicationDidReceiveMemoryWarning(application: UIApplication) {
    NSURLCache.sharedURLCache().removeAllCachedResponses()
}

我还尝试了在完成块中使用以下文章(removeFromSuperview),但我也无法使其工作(请参见上面的代码):

https://www.hackingwithswift.com/example-code/uikit/how-to-animate-views-using-animatewithduration

新代码:
// create the Animation Array for each animation

func createImageArray(total: Int, imagePrefix: String) -> [UIImage]{
    var imageArray: [UIImage] = []
    for imageCount in 0..<total {
        let imageName = "\(imagePrefix)-\(imageCount).png"
        //let image = UIImage(named: imageName)! // replaced with your code below

    if let imagePath = Bundle.mainBundle.path(forResource: "ImageSet1" ofType: "png"),
    let image = UIImage(contentsOfFile: imagePath) {
         //Your image has been loaded   }

        imageArray.append(image)
    }
    return imageArray }

1
你说:“但是在我的代码中似乎不起作用。”这是什么意思?你还在被终止吗?你能看到动画吗? - mugx
1
嗨@AndreaMugnaini - 我已经尝试了各种文章中提供的建议答案(请参见标有//的注释),但是我仍然被终止。动画效果很完美,但是随着我添加每个额外的动画,总体内存使用量会增加。我以为我可以简单地使用self.animationView.removeFromSuperview()来清除动画,但这似乎也不起作用(内存保持不变)。希望这有助于澄清。 - Server Programmer
1
请问您能检查一下您的PNG文件大小吗? - mugx
1
嗨 @AndreaMugnaini - 平均 PNG 文件大小为 100kbs - Server Programmer
1个回答

6
很简单。 UIImage(named:) 会缓存图像,UIImage(contentsOfFile:) 则不会。
如果你不想让你的图像被缓存,可以使用 UIImage(contentsOfFile:)。 如果你无法让它起作用,请发布您的代码,我们将帮助您进行调试。
请注意,UIImage(contentsOfFile:) 不会在应用程序包中查找文件。 它需要一个图像文件的完整路径。 您需要使用 Bundle 方法查找文件的路径,然后将该路径传递给UIImage(contentsOfFile:)
if let imagePath = Bundle.mainBundle.path(forResource: "ImageSet1" ofType: "png"),
  let image = UIImage(contentsOfFile: imagePath) {
     //Your image has been loaded 
}

您的代码将所有3个动画的所有图像加载到一个图像数组中,并永远不释放它们,因此系统缓存这些图像似乎并不重要。在低内存条件下,系统应该刷新图像的缓存副本,但是您的代码仍将保留所有这些图像在您的数组中,因此内存不会被释放。在我看来,是您的代码导致了内存问题,而不是系统的图像缓存。

您的代码可能如下所示:

func createImageArray(total: Int, imagePrefix: String) -> [UIImage]{
    var imageArray: [UIImage] = []
    for imageCount in 0..<total {
        let imageName = "\(imagePrefix)-\(imageCount)"
        if let imagePath = Bundle.main.path(forResource: imageName,
            ofType: "png"),
          let image = UIImage(contentsOfFile: imagePath) {
              imageArray.append(image)
        }
    }
    return imageArray 
}

1
不,我会让你自己解决。我不会在SO答案中提供复制粘贴的代码。如果你将我的答案适应到你的代码中,你会学到更多。 - Duncan C
1
如果您想释放内存,需要释放图像的数组。这些数组持有对图像的强引用,这就是为什么您的内存占用量没有降低的原因。 - Duncan C
1
在问题的结尾处添加新的UIImage(contentsOfFile:)更改。你描述的错误听起来像是你的代码被编写成返回一个闭包,而不是一个值。 - Duncan C
1
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/160867/discussion-between-duncan-c-and-server-programmer。 - Duncan C
嗨,@Duncan C - 我不得不创建一个新的组并将图像移出xassets文件夹。一切都很好 - 感谢您的帮助。 - Server Programmer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接