SDWebImage GIF图加载


### iOS开发中的GIF图加载

GIF是图片的一种压缩格式，其实就是将多张具有一定存活时间的静态图片压缩组合在了一起，在展示时将这些静态图片按照播放时长连贯播放就形成了动画

* webVIew加载
 wkwebview
` [webView loadData:gif MIMEType:@"image/gif" characterEncodingName:nilbaseURL:nil];
`
 uiwebview
` [webView loadData:gif MIMEType:@"image/gif" textEncodingName:@"UTF-8" baseURL:nil];
`

* UIImageView加载  
UIImageView控件并不能直接支持GIF，可以使用ImageIO.framework的API可以实现播放
获取gifData的imageSource
`    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)_gifData, NULL);
`
 获取图片的帧数 size_t count = CGImageSourceGetCount(source);获取image数组
然后通过imageView.animationImages = images，最后调startAnimating即可简单实现动图效果。

这里仅讨论UIImageView加载GIF的情况。
虽然实现了通过UIImageView来播放GIF，但存在问题：

1. images数组是准备好才开始播放的，一旦图片很多很大会造成内存急剧上升,并且造成卡顿。
	
	对于内存问题，想到的解决方案是获取图片每一帧，自己控制每一帧的切换，通过NSTimer或者CADisplayLink等方式实现。

	对于卡顿，猜想是图片在屏幕上显示时，因为压缩的图像数据会在渲染时被解码成其未压缩的位图形式，这是一个非常耗费CPU的操作，而且默认在主线程上做；可以考虑在子线程提前将图片解码，不过这个会带来内存的损耗，记得之前instrument查看内存时会出现很多VM: CG Raster data ，这也是之前SD库中经常出现的内存问题，因为默认开启了解压操作，下载的图片默认都解压然后缓存。decodedImageWithImage: 每一个像素点都会分配一个空间来存储相关值，那么分辨率越高的图片，就意味着更多数量的像素点，也就意味着需要分配更多的空间，内存开销极大。
	

2. 播放的时间只能大概估计，不能很准确地表达出真正时长下的播放效果
	
	想获取真实的播放时长，必须获取GIF图中的附加信息，通过CGImageSourceCopyPropertiesAtIndex方法可以获取souceRef中指定帧（index）的属性字典，我们可以知道每一帧的一些属性 （见CGImageProperties.h）kCGImagePropertyGIFDelayTime即为播放时间。


看看浏览器之前关于GIF图片的处理

* 首页顶部动态图片
* 看图模式

### 关于GIF加载的第三方库
这里主要讨论SDWebImage库

### SD各个版本对比

#### SDWebImage4.0 以前版本
加载一个GIF图的url，显示的是<font  color =  '#FF0000'>静态的图片</font>

#### SDWebImage4.x 
采用FLAnimatedImage

FLAnimatedImage渲染流程
![](FLAnimatedImageView.jpg)

FLAnimatedImage 会有两个线程同时在运转。 其中一个线程负责渲染 GIF 的每一帧的图片内容（所谓的渲染，大体上就是加载GIF文件数据，然后抽取出来当前需要哪一帧）。这个加载图片的过程是在异步线程进行的。然后 FLAnimatedImage 会有一个内存区域专门放置这些渲染好的帧。 这时候，在主线程中的UIImageView会根据当前需要，从这个内存区域中读取相应的帧。
这样最大限度的保证主线程不去处理图片渲染的操作，并且我们刚才说的那个内存区域， 也是经过一系列的动态管理，以提升性能。 并且 FLAnimatedImage 的渲染线程，在数据没有准备好的时候，不会加锁等待，而是直接返回 nil。 这样也能避免一些实现中为了图片序列完全显示正确，而造成性能卡顿的问题。

dispatch_main_async_safe(block) 保证 block 在主线程中执行，其中包含 setImageBlock。因此 setImageBlock 在主线程中执行，也就是说 FLAnimatedImage 在主线程中生成，这一步比较耗时，阻塞主线程，造成卡顿。
解决办法是，把FLAnimatedImage的创建放到子线程中。

给FLAnimatedImageView添加扩展方法
    
    func setImage(with url: URL?, placeholderImage: UIImage?) {
        sd_internalSetImage(with: url, placeholderImage: placeholderImage, options: SDWebImageOptions(rawValue: 0), operationKey: nil, setImageBlock: { [weak self] (image, imageData) in
            guard let strongSelf = self else { return }
            
            let imageFormat = NSData.sd_imageFormat(forImageData: imageData)
            if imageFormat == .GIF {
            	// Enter global queue
                DispatchQueue.global(qos: .userInteractive).async { [weak self] in
                	// Create FLAnimatedImage in global queue
                    let animatedImage = FLAnimatedImage(animatedGIFData: imageData)
                    DispatchQueue.main.async { [weak self] in
                        guard let strongSelf = self else { return }
                        // Set image in main queue
                        strongSelf.animatedImage = animatedImage
                        strongSelf.image = nil
                    }
                }
            } else {
            	// Set image in main queue
                strongSelf.image = image
                strongSelf.animatedImage = nil
            }
            }, progress: nil, completed: nil)
    }


同样调用 sd_internalSetImageWithURL: 方法，只是修改 setImageBlock 参数，在子线程中创建 FLAnimatedImage，然后在主线程中设置图片。

#### SDWebImage5.x 

新推出 SDAnimatedImageView类






