Ios 在UIImageView中加载gif图像时减少内存消耗

Ios 在UIImageView中加载gif图像时减少内存消耗,ios,swift,memory,uiimageview,gif,Ios,Swift,Memory,Uiimageview,Gif,我想在UIImageView中显示gif图像,使用下面的代码(源代码:,*我不理解所有代码),我可以显示gif图像。然而,内存消耗似乎很高(在实际设备上测试)。有没有办法修改下面的代码以减少内存消耗 @IBOutlet weak var imageView: UIImageView! override func viewDidLoad() { super.viewDidLoad() let url = "https://cdn-images-1.medium.com/max/

我想在UIImageView中显示gif图像,使用下面的代码(源代码:,*我不理解所有代码),我可以显示gif图像。然而,内存消耗似乎很高(在实际设备上测试)。有没有办法修改下面的代码以减少内存消耗

@IBOutlet weak var imageView: UIImageView!

override func viewDidLoad() {
    super.viewDidLoad()
    let url =  "https://cdn-images-1.medium.com/max/800/1*oDqXedYUMyhWzN48pUjHyw.gif"
    let gifImage = UIImage.gifImageWithURL(url)
    imageView.image = gifImage 
}

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

fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
    switch (lhs, rhs) {
    case let (l?, r?):
        return l < r
    case (nil, _?):
        return true
    default:
        return false
    }
}

extension UIImage {
   public class func gifImageWithData(_ data: Data) -> UIImage? {
        guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
            print("image doesn't exist")
            return nil
        }
            return UIImage.animatedImageWithSource(source)
    }

    public class func gifImageWithURL(_ gifUrl:String) -> UIImage? {
        guard let bundleURL:URL? = URL(string: gifUrl) else {
            return nil
        }
        guard let imageData = try? Data(contentsOf: bundleURL!) else {
            return nil
        }
        return gifImageWithData(imageData)
    }

    public class func gifImageWithName(_ name: String) -> UIImage? {
        guard let bundleURL = Bundle.main
        .url(forResource: name, withExtension: "gif") else {
            return nil
        }
        guard let imageData = try? Data(contentsOf: bundleURL) else {
            return nil
        }
        return gifImageWithData(imageData)
    }

    class func delayForImageAtIndex(_ index: Int, source: CGImageSource!) -> Double {
        var delay = 0.1
        let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
        let gifProperties: CFDictionary = unsafeBitCast(
        CFDictionaryGetValue(cfProperties,
                             Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque()),
        to: CFDictionary.self)

        var delayObject: AnyObject = unsafeBitCast(
        CFDictionaryGetValue(gifProperties,
                             Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()),
        to: AnyObject.self)
        if delayObject.doubleValue == 0 {
            delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,
                                                         Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self)
        }

        delay = delayObject as! Double

        if delay < 0.1 {
            delay = 0.1
        }

        return delay
    }

    class func gcdForPair(_ a: Int?, _ b: Int?) -> Int {
        var a = a
        var b = b
        if b == nil || a == nil {
            if b != nil {
                return b!
            } else if a != nil {
                return a!
            } else {
                return 0
            }
        }

        if a < b {
            let c = a
            a = b
            b = c
        }

        var rest: Int
        while true {
            rest = a! % b!

            if rest == 0 {
               return b!
            } else {
                a = b
                b = rest
            }
        }
    }

    class func gcdForArray(_ array: Array<Int>) -> Int {
        if array.isEmpty {
            return 1
        }

        var gcd = array[0]

        for val in array {
            gcd = UIImage.gcdForPair(val, gcd)
        }

       return gcd
    }

    class func animatedImageWithSource(_ source: CGImageSource) -> UIImage? {
        let count = CGImageSourceGetCount(source)
        var images = [CGImage]()
        var delays = [Int]()

        for i in 0..<count {
            if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
                images.append(image)
            }

            let delaySeconds = UIImage.delayForImageAtIndex(Int(i),
                                                        source: source)
            delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
        }

        let duration: Int = {
            var sum = 0

            for val: Int in delays {
                sum += val
            }

            return sum
         }()

        let gcd = gcdForArray(delays)
        var frames = [UIImage]()

        var frame: UIImage
        var frameCount: Int
        for i in 0..<count {
            frame = UIImage(cgImage: images[Int(i)])
            frameCount = Int(delays[Int(i)] / gcd)

            for _ in 0..<frameCount {
                frames.append(frame)
            }
        }

        let animation = UIImage.animatedImage(with: frames,
                                          duration: Double(duration) / 1000.0)

        return animation
    }
}
@ibvar-imageView:UIImageView!
重写func viewDidLoad(){
super.viewDidLoad()
让url=”https://cdn-images-1.medium.com/max/800/1*oDqXedYUMyhWzN48pUjHyw.gif“
让gifImage=UIImage.gifImageWithURL(url)
imageView.image=gifImage
}
重写函数didReceiveMemoryWarning(){
超级。我收到了记忆警告()
//处置所有可以重新创建的资源。
}
fileprivate func<(左:T?、右:T?->Bool{
开关(左、右){
小木箱(l?,r?):
返回lUIImage{
guard let source=CGImageSourceCreateWithData(数据为CFData,无)else{
打印(“图像不存在”)
归零
}
返回UIImage.animatedImageWithSource(源)
}
公共类func gifImageWithURL(gifull:String)->UIImage{
guard let bundleURL:URL?=URL(字符串:gifUrl)else{
归零
}
guard let imageData=try?Data(内容:bundleURL!)其他{
归零
}
返回gifImageWithData(图像数据)
}
公共类func-gifImageWithName(u-name:String)->UIImage{
guard let bundleURL=Bundle.main
.url(forResource:name,扩展名为:“gif”)else{
归零
}
guard let imageData=try?数据(内容:bundleURL)else{
归零
}
返回gifImageWithData(图像数据)
}
类func delayForImageAtIndex(uIndex:Int,source:cImageSource!)->Double{
var延迟=0.1
让cfProperties=CGImageSourceCopyPropertiesAtIndex(源,索引,无)
让属性:CFDictionary=unsafeBitCast(
CFDictionaryGetValue(cfProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFDictionary.toOpaque()),
收件人:CFDictionary.self)
var delayObject:AnyObject=unsafeBitCast(
CFDictionaryGetValue(gifProperties,
未托管.passUnrepeated(KCGimagePropertyGifUnampledDelayTime).toOpaque()),
收件人:AnyObject.self)
如果delayObject.doubleValue==0{
delayObject=unsafeBitCast(CFDictionaryGetValue)(GIF属性,
Unmanaged.passUnrepaired(kCGImagePropertyGIFDelayTime.toOpaque()),收件人:AnyObject.self)
}
延迟=延迟对象为!双精度
如果延迟<0.1{
延迟=0.1
}
返回延迟
}
类函数gcdForPair(uA:Int?,b:Int?)->Int{
变量a=a
变量b=b
如果b==nil | | a==nil{
如果b!=nil{
返回b!
}否则,如果a!=零{
还一个!
}否则{
返回0
}
}
如果aInt{
如果array.isEmpty{
返回1
}
var gcd=数组[0]
对于数组中的val{
gcd=UIImage.gcdForPair(val,gcd)
}
返回gcd
}
类func animateImageWithSource(uSource:CGImageSource)->UIImage{
let count=CGImageSourceGetCount(源)
var images=[CGImage]()
变量延迟=[Int]()

对于0..中的i,所讨论的GIF分辨率为480×288,包含10帧

考虑到
UIImageView
将帧存储为4字节RGBA,此GIF在RAM中占用4×10×480×288=5 529 600字节,超过5 MB

有许多方法可以减轻这种压力,但其中只有一种方法不会给CPU带来额外的压力;其他方法只是CPU与RAM之间的权衡

我所说的方法是将
UIImageView
子类化,并手动加载GIF,保留其内部表示(索引图像+调色板)。这样可以将内存使用量减少四倍

注意:尽管GIF可以存储为每一帧的完整图像(这是GIF的情况),但很多都不是。相反,大多数帧只能包含自上一帧以来已更改的像素。因此,通常内部GIF表示只允许按直接顺序显示帧


保存RAM的其他方法包括:在显示前从磁盘上重新读取每一帧,这肯定不利于电池寿命。

要以较少的内存消耗显示GIF,请尝试

BBWebImage将根据当前内存使用情况决定解码和缓存多少图像帧。如果可用内存不足,则仅解码和缓存部分图像帧

对于Swift 4:

//BBAnimatedImageView(UIImageView子类)显示动画图像
imageView=BBAnimatedImageView(帧:帧)
//加载和显示gif
imageView.bb_setImage(带:url,
占位符:UIImage(名为:“占位符”))
{(图像:UIImage?,数据:data?,错误:error?,缓存类型:BBImageCacheType)在中
//完成装载后做些什么
}

当您评论gif时,它使用了多少内存code@sanjaykmwt我现在已经在真实设备中进行了测试,并在t中更新了这两种情况下的消耗量