SDWeb Image Loading Display GIF and Performance Problems

Posted by BluePhoenixNC on Thu, 11 Jul 2019 22:12:07 +0200

SDWeb Image Loading Display GIF and Performance Problems

Prior to SDWebImage 4.0, GIF diagrams could be displayed using UI Image View. If SDWebImage 4.0 does the same, it will only show static images. SDWebImage 4.0 uses FLAnimated Image View to display GIF diagrams through FLAnimated Image. The two libraries in this article are SDWebImage 4.0.0 and FLAnimatedImage 1.0.12, respectively.

CocoaPods Installation

pod 'SDWebImage'
pod 'SDWebImage/GIF'

General usage

Display GIF with FLAnimated Image View instead of UI Image View. Uses introduced in README.md of FLAnimatedImage

FLAnimatedImage *image = [FLAnimatedImage animatedImageWithGIFData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://upload.wikimedia.org/wikipedia/commons/2/2c/Rotating_earth_%28large%29.gif"]]];
FLAnimatedImageView *imageView = [[FLAnimatedImageView alloc] init];
imageView.animatedImage = image;
imageView.frame = CGRectMake(0.0, 0.0, 100.0, 100.0);
[self.view addSubview:imageView];

Don't write that, this code will block the main thread! The main thread obtains NSData through the URL and waits until the download is complete to execute the next step.

FLAnimatedImage View is similar to UIImage View in that it initializes, sets frame s, adds to views, and assigns values to image attributes to display static images; unlike FLAnimatedImage, it assigns values to animatedImage attributes to display dynamic images. The problem with the above code is the generation of FLAnimatedImage.

SDWebImage adds an asynchronous loading method of GIF to FLAnimatedImage View, just like asynchronous loading of static images.

imageView.sd_setImage(with: url, placeholderImage: placeholder)

If a small amount of GIF is displayed, it should be possible to write in this way. However, if you need to show a large number of GIFs with UITableView or UICollection View, there may be performance problems and a stuck when sliding.

Improving performance

To improve performance, you can specify RunLoopMode, animate in default mode, and stop animation in tracking mode (such as scroll view sliding).

imageView.runLoopMode = RunLoopMode.defaultRunLoopMode.rawValue

In my code, there will still be a Dunka. Looking at the source code of SDWebImage, we found the problem.

sd_setImage(with:placeholderImage:) calls sd_internalSetImageWithURL: method

Note that the setImageBlock parameter in the sd_internalSetImageWithURL: method generates FLAnimatedImage here. Further look at sd_internalSetImageWithURL: Implementation of the method

The macro definition dispatch_main_async_safe(block) guarantees that the block is executed in the main thread, which contains setImageBlock. So setImageBlock is executed in the main thread, that is to say, FLAnimatedImage is generated in the main thread. This step is time-consuming, blocking the main thread and causing the Dunka.

The solution is to put the generation of FLAnimatedImage in a sub-thread. SDWebImage source code can be modified directly, but this is not recommended. A better way is to add methods to FLAnimated Image View

extension 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
            // Enter global queue
            DispatchQueue.global(qos: .userInteractive).async { [weak self] in
                guard self != nil else { return }
                
                let imageFormat = NSData.sd_imageFormat(forImageData: imageData)
                if imageFormat == .GIF {
                    // 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 {
                    DispatchQueue.main.async { [weak self] in
                        guard let strongSelf = self else { return }
                        // Set image in main queue
                        strongSelf.image = image
                        strongSelf.animatedImage = nil
                    }
                }
            }
            }, progress: nil, completed: nil)
    }
}

Also call sd_internalSetImageWithURL: method, just modify the setImageBlock parameter, create FLAnimatedImage in the sub-thread, and then set the picture in the main thread.

Calling this method is simple

imageView.setImage(with: url, placeholderImage: placeholder)

In this way, the UITableView slides smoothly.

The code has been uploaded to GitHub: https://github.com/Silence-GitHub/GIFDemo

For reprinting, please indicate the source: http://www.cnblogs.com/silence-cnblogs/p/6682867.html

Topics: iOS github