我在iPad应用程序上遇到一个问题,即所有显示的图像(全部为1024x768)都会占用RAM而不会在不需要时释放它们,导致一段时间后崩溃。
我使用“contentsOfFile”方法加载图像,这应该(在我的理解中)释放内存时内存不足并且应用程序不再需要它但它似乎不是这种情况(即使我模拟)模拟器上的内存警告。)
我制作了一个非常简单的测试类来说明下面的问题(swift 1.2)。
当应用程序加载时,它会创建一个视图,添加填充图像的子图层,然后当我点击屏幕时,我希望未压缩的位图数据(由Core Animation用于渲染图层)获取的72Mo被释放(或者至少在我模拟记忆警告时被释放),它从来没有这样做。
(注意:我在我的主项目和这个测试项目中使用ARC)
ViewController: UIViewController {
func tap(gesture : UITapGestureRecognizer)
{
if gesture.state == UIGestureRecognizerState.Ended {
var tmpV = self.view.viewWithTag(1000)
// tried everything to release memory
for subLayer : CALayer in tmpV?.layer.sublayers as! [CALayer!] {
subLayer.contents = nil
}
tmpV?.layer.sublayers.removeAll(keepCapacity: false)
tmpV?.removeFromSuperview()
tmpV = nil
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
//add gesture to remove images from view
var gesture = UITapGestureRecognizer(target: self, action: Selector("tap:"))
self.view.addGestureRecognizer(gesture)
//loading images
var imagesArray : [UIImage!]! = self.fillImagesArray()
//adding those images to a view
let tmpV = UIView(frame: CGRect(x: 0, y: 0, width: 1024, height: 768))
for img in imagesArray {
var layer = CALayer()
layer.frame = CGRect(x: 0, y: 0, width: 1024, height: 768)
layer.contents = img.CGImage
tmpV.layer.addSublayer(layer)
}
tmpV.tag = 1000
self.view.addSubview(tmpV)
}
func fillImagesArray() -> [UIImage!]{
var array : [UIImage!] = []
for i in 0 ... 23 {
var image : UIImage?
var filename = NSBundle.mainBundle().pathForResource("image \(i)", ofType: "png")
image = UIImage(contentsOfFile: filename!)
array.append(image)
}
return array
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
编辑:
要添加更多细节,这里是从Profile / Allocations工具截取的屏幕截图,正如我所说的,我想在渲染图层时CA会占用内存。
当图像没有显示在屏幕上时,内存仍然很低,但是一旦显示它们,即使在删除后它们也不会被释放。
我尝试绘制tmpV图层,删除子图层,然后将绘制的图像添加到viewDidAppear
中的tmpV.layer.contents,如果我这样做,我的图像是正确的,内存保持不变但成本大约1.5秒绘制图层(我需要我的应用程序快速,我这次不能负担)。
我发现释放内存的唯一方法是将图像添加到类变量数组中,并在tap
func上清空它。但是如果我想重新创建一个图像,我需要重新加载图像,我想避免这种情况。
答案 0 :(得分:1)
程序员负责与contentsOfFile
关联的所有内存使用情况。如果您对图像对象有强烈的引用,比如说在数组中,那么在从数组中删除它并且没有对它的强引用之前,它将不会从内存中删除。
您可能需要考虑使用NSCache
及其关联的NSDiscardableContent
协议来控制图片。
答案 1 :(得分:1)
你说:
我使用" contentOfFile"加载图片应该的方法(在我的 理解)当内存不足和app时释放内存 不再需要了......
(应该是内容 s OfFile,带有" s")
那不太正确。加载图像的另一种常见方式是imageNamed,可以缓存它加载的图像,以备再次使用时。
用于加载图像的contentsOfFile方法不进行任何缓存。它会将您带回图像,然后您需要保持对图像的强引用,否则它将被取消分配。您需要跟踪对图像的所有强引用,并确保在完成图像后将它们全部缩小。
让我们浏览您发布的代码和图片的所有权(以及这些图片中的CGImage数据。)
将图像存储在fillImagesArray方法范围内定义的数组中。该方法返回数组,因此调用者将获得该数组的所有权。您将图像数组存储在viewDidAppear范围内的另一个局部变量imagesArray中。应该在viewDidAppear返回时释放该数组。
您可以将图像中的CGImage数据安装到一组图层中,这些图层将作为您创建的视图(tmpV)的子图层安装。
当viewDidAppear返回时,应释放原始UIImage对象并取消分配。但是,图像数据现在存储在一堆层中。
然后将tmpV安装为视图控制器内容视图的子视图。
因此,完成所有操作后,图像数据将归您的图层所有。这些图层由您的视图tmpV拥有的视图图层所有。该视图归视图控制器的内容视图所有。我没有看到图像数据的任何其他所有权。强引用链具有单一点,即您的tmpV视图已添加为视图控制器内容视图的子视图。
所以,你应该能够通过从它的超级视图中删除tmpV来摆脱整个套件和caboodle。如果可行,则应取消分配数据。
您不需要执行将图层数据归零并删除图层的其他工作。事实上,它可能会导致问题。我会删除所有代码。特别是,清空子层数组的调用看起来对我来说是一个坏主意。如果您要遍历图层数组,我建议您在每个图层上调用removeFromSuperlayer(尽管您需要在枚举之前复制图层数组,这样它就不会发生变异它被列举。)
您应该记录通过标记获取tmpV视图的代码,以确保它找到视图。
你怎么断定你的记忆没有被释放?你在哪里测量它?