尝试加载200多张图像时出现内存警告

时间:2014-11-23 15:35:14

标签: ios swift uiimage

更新:

现在只使用CollectionView。我应该从一开始就这样做,但我对iOS很新。 CollectionViews为您管理所有图像的获取和发布。所以没有内存麻烦。在互联网上有足够的教程。快速说明一下。为图像指定一个带有数字的名称,并且不要在该数字前添加零。然后你可以使用indexPath获取它们。

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


        cell.imageView?.image = UIImage(named: "bg_\(indexPath.row)")


    return cell
}

我正在为图形小说构建图像查看应用程序。一个拥有200多张图像的巨型滚动视图。

我尝试根据示例代码构建一些内存管理,但这似乎不起作用。我想它不起作用,因为我只对图像进行内存管理,而不是对它们进行查看。

这是我需要使用更多"弱"变量以及如何使这些变量起作用?

当我把变量放入变量时,它告诉我它需要一个类。

import UIKit

class ViewController: UIViewController, UIScrollViewDelegate  {

    @IBOutlet var scrollView: UIScrollView!


    var imageGroup : [UIImage?] = []
    var containerViews : [UIImageView?] = []
    var containerView :UIImageView?
    var imgWidthMult : CGFloat = 2.121875
    let imageGroupCount : CGFloat?
    let containerHeight : CGFloat?
    let containerWidth : CGFloat?
    var imageCounter : Int = 0



    override func viewDidLoad() {
        super.viewDidLoad()
        // 1
        imageGroup = [
            UIImage(named: "bg_001")!,
            UIImage(named: "bg_002")!,
            UIImage(named: "bg_003")!,
            UIImage(named: "bg_004")!,
            //200+ images to follow here
        ]

        let imageGroupCount = CGFloat(imageGroup.count)
        println(imageGroupCount)


        // 3
        for i in 0..<imageGroup.count {
            containerViews.append(nil)
        }

        // 4

        let imagesScrollViewSize = UIScreen.mainScreen().applicationFrame;
        scrollView.contentSize = CGSizeMake(imagesScrollViewSize.height * imgWidthMult * CGFloat(imageGroup.count), imagesScrollViewSize.height)

        // 5
        let containerframe = UIScreen.mainScreen().applicationFrame;
        let containerHeight : CGFloat = containerframe.height
        let containerWidth : CGFloat = (imgWidthMult * containerHeight)

        loadVisibleImages()

        println("containerWidth")
        println(containerWidth)
        println(containerframe.size)
        println(scrollView.contentSize)




        return
    }

    // this loads images and should be adjusted and taken out of local scope


    func loadImage (imageCounter:Int) {
        let containerframe = UIScreen.mainScreen().applicationFrame;
        let containerHeight : CGFloat = containerframe.height
        let containerWidth : CGFloat = (imgWidthMult * containerHeight)

        if imageCounter < 0 || imageCounter >= imageGroup.count {
            // If it's outside the range of what you have to display, then do nothing
            return
        }

        // 1
        if let containerView = containerViews[imageCounter] {
            // Do nothing. The view is already loaded.
        } else {
            // 2
            var frame = UIScreen.mainScreen().applicationFrame;
            frame.origin.x = frame.size.height * CGFloat(imageCounter) * 2.121875
            frame.origin.y = 0.0
            frame.size = CGSize(width: containerWidth, height: containerHeight)

            // 3
            var newcontainerView = UIImageView(image: imageGroup[imageCounter])
            newcontainerView.contentMode = .ScaleAspectFit
            newcontainerView.frame = frame
            scrollView.addSubview(newcontainerView)


            containerViews[imageCounter] = newcontainerView
        }

    }

    func purgeImage(imageCounter:Int) {

        if imageCounter < 0 || imageCounter >= imageGroup.count {
            // If it's outside the range of what you have to display, then do nothing
            return
        }

        // Remove a page from the scroll view and reset the container array
        if let containerView = containerViews[imageCounter] {
            containerView.removeFromSuperview()
            containerViews[imageCounter] = nil
            println("removed page")
        }

    }


    func loadVisibleImages() {


        // First, determine which page is currently visible
        let containerframe = UIScreen.mainScreen().applicationFrame;
        let containerHeight : CGFloat = containerframe.height
        let containerWidth : CGFloat = (imgWidthMult * containerHeight)
        let pagesWidth = (containerWidth)
        let imageCounter = Int(floor((scrollView.contentOffset.x * 2.0 + pagesWidth) / (pagesWidth * 2.0)))


        println(Int(floor((scrollView.contentOffset.x * 2.0 + pagesWidth) / (pagesWidth * 2.0))))
        println(pagesWidth)
        println(containerHeight)


        println(imageCounter)

        // Update the page control

        // Work out which pages you want to load
        let firstPage = imageCounter - 1
        let lastPage = imageCounter + 1


        // Purge anything before the first page
        for var index = 0; index < firstPage; ++index {
            purgeImage(index)
        }

        // Load pages in our range
        for var index = firstPage; index <= lastPage; ++index {
            loadImage(index)
        }

        // Purge anything after the last page
        for var index = lastPage+1; index < imageGroup.count; ++index {
            purgeImage(index)
        }
        println("loadVisibleImages")
    }





    func scrollViewDidScroll(scrollView: UIScrollView) {
        // Load the pages that are now on screen
        println("did scroll")
        loadVisibleImages()
        println("did scroll")

    }




    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

编辑:

import UIKit

class ViewController: UIViewController, UIScrollViewDelegate  {

    @IBOutlet var scrollView: UIScrollView!


    var containerViews : [UIImageView?] = []
    var containerView : UIImageView?
    var imgWidthMult : CGFloat = 2.121875
    let imageGroupCount : Int = 149
    let containerHeight : CGFloat?
    let containerWidth : CGFloat?
    var imageCounter : Int = 000
    var loadingVisibleImages : Bool?


    override func viewDidLoad() {
        super.viewDidLoad()
        // 1


        loadingVisibleImages = false

        // 3
        for i in 0..<imageGroupCount {
            containerViews.append(nil)
        }

        // 4

        let imagesScrollViewSize = UIScreen.mainScreen().applicationFrame;
        scrollView.contentSize = CGSizeMake(imagesScrollViewSize.height * imgWidthMult * CGFloat(imageGroupCount), imagesScrollViewSize.height)

        // 5

        let containerframe = UIScreen.mainScreen().applicationFrame;
        let containerHeight : CGFloat = containerframe.height
        let containerWidth : CGFloat = (imgWidthMult * containerHeight)

        loadVisibleImages()


        return
    }

    // this loads images and should be adjusted and taken out of local scope


    func loadImage (imageCounter:Int) {
        let containerframe = UIScreen.mainScreen().applicationFrame;
        let containerHeight : CGFloat = containerframe.height
        let containerWidth : CGFloat = (imgWidthMult * containerHeight)

        if imageCounter < 0 || imageCounter >= imageGroupCount {
            // If it's outside the range of what you have to display, then do nothing
            return
        }

        // 1
        if let containerView = containerViews[imageCounter] {
            // Do nothing. The view is already loaded.
        } else {
            // 2
            var frame = UIScreen.mainScreen().applicationFrame;
            frame.origin.x = ((frame.size.height * CGFloat(imageCounter) * 2.121875) + containerframe.width)
            frame.origin.y = 0.0
            frame.size = CGSize(width: containerWidth, height: containerHeight)

            // 3
            let newImage = UIImage(named: "bg_\(imageCounter + 1)")
            var newcontainerView = UIImageView(image: newImage)
            newcontainerView.contentMode = .ScaleAspectFit
            newcontainerView.frame = frame
            scrollView.addSubview(newcontainerView)


            containerViews[imageCounter] = newcontainerView
        }

    }

    func purgeImage(imageCounter:Int) {

        if imageCounter < 0 || imageCounter >= imageGroupCount {
            // If it's outside the range of what you have to display, then do nothing
            return
        }

        // Remove a page from the scroll view and reset the container array
        if let containerView = containerViews[imageCounter] {
            containerView.removeFromSuperview()
            containerViews[imageCounter] = nil
            println("removed page")
        }

    }


    func loadVisibleImages() {

        loadingVisibleImages = true

        // First, determine which page is currently visible
        let containerframe = UIScreen.mainScreen().applicationFrame;
        let containerHeight : CGFloat = containerframe.height
        let containerWidth : CGFloat = (imgWidthMult * containerHeight)
        let pagesWidth = (containerWidth)
        let imageCounter = Int(floor(((scrollView.contentOffset.x * 2.0 + pagesWidth) / (pagesWidth * 2.0)))


        println(pagesWidth)
        println(containerHeight)


        println(imageCounter)

        // Update the page control

        // Work out which pages you want to load
        let firstPage = imageCounter - 1
        let lastPage = imageCounter + 1


        // Purge anything before the first page
        for var index = 0; index < firstPage; ++index {
            purgeImage(index)
        }

        // Load pages in our range
        for var index = firstPage; index <= lastPage; ++index {
            loadImage(index)
        }

        // Purge anything after the last page
        for var index = lastPage+1; index < imageGroupCount; ++index {
            purgeImage(index)
        }

        loadingVisibleImages = false
        println("loadVisibleImages")
    }





    func scrollViewDidScroll(scrollView: UIScrollView) {
        // Load the pages that are now on screen
        println("did scroll")
        if loadingVisibleImages == false {
        loadVisibleImages()
        }


    }

    func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
        if loadingVisibleImages == false {
            loadVisibleImages()
        }
    }




    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

2 个答案:

答案 0 :(得分:3)

您的内存管理问题。不是斯威夫特的。这是你出错的地方......

viewDidLoad方法中,您已经实例化了包含200多张图片的imageGroup数组。假设这些图像不是非常小,那么你已经从get go中为一个不必要的数组分配了一个巨大的内存块。您应该在loadImage方法中根据需要获取这些图片。

例如,您可以替换此行:

var newcontainerView = UIImageView(image: imageGroup[imageCounter])

带有以下内容:

let newImage = UIImage(named: "bg_\(imageCounter + 1)")
var newcontainerView = UIImageView(image: newImage)

因此您可以根据需要从捆绑中获取图像。

注意:您需要找出适合您的情况的适当的文件名获取算法... named: "bg_\(imageCounter + 1)"只是一个例子。

至于您在整个代码中使用imageGroup.count,因为您的viewDidLoad显示您的图片已知数量,我建议您更换imageGroup.count的所有实例等效常数。


编辑:

要解决代码中的另一个问题,在loadVisibleImages中调用scrollViewDidScroll也会导致内存问题。

当滚动视图滚动时,UIScrollView委托方法scrollViewDidScroll会被连续调用,因此,正如您的代码所示,loadVisibleImages也将被调用。因此,特别是当用户快速滚动并且您的应用同时运行loadVisibleImages的多次迭代时(方法的一次传递在下一次开始之前尚未完成),这可能会导致此特定情况下的崩溃。

所以这里有一个建议,现在就在我的脑海中(我可以很好地提出一个更好的一个,我相信更好的存在)。在您的scrollViewDidScroll方法中,如果您的应用尚未通过,则可能只调用loadVisibleImages。然后在滚动视图完成减速后再次将其作为安全措施,以确保您拥有可见图像。例如:

var loadingVisibleImages

override func viewDidLoad() {
    super.viewDidLoad()

    loadingVisibleImages = false
    ...
}

func loadVisibleImages() {
    loadingVisibleImages = true
    ...
    loadingVisibleImages = fasle
}

func scrollViewDidScroll(scrollView: UIScrollView) {
    // Load the pages that are now on screen
    println("did scroll")
    if loadingVisibleImages == false
        loadVisibleImages()
}

func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
    if loadingVisibleImages == false
        loadVisibleImages()
}

答案 1 :(得分:1)

使用可重复使用的滚动视图第三方框架。它加载和显示图像的方式与表格视图类似。它在每个滚动条上使用可重用的视图。您可以显示小尺寸的占位符,并在焦点上重新加载图像的实际大小。非常容易使用: https://github.com/sumofighter666/ReusableScrollView