Repository: SDWebImage/SDWebImage
File: SDWebImage/SDAnimatedImageView.m (Objective-C)

Code before:
{
    self.isProgressive = NO;
    if (!self.shouldIncrementalLoad) {
        // Early return
        return;
    }
    if ([image conformsToProtocol:@protocol(SDAnimatedImage)] && image.sd_isIncremental) {
        UIImage *previousImage = self.image;
        if ([previousImage conformsToProtocol:@protocol(SDAnimatedImage)] && previousImage.sd_isIncremental) {
            NSData *previousData = [((UIImage<SDAnimatedImage> *)previousImage) animatedImageData];
            NSData *currentData = [((UIImage<SDAnimatedImage> *)image) animatedImageData];
            // Check whether to use progressive rendering or not
            if (!previousData || !currentData) {
                // Early return
                return;
            }
            
            // Warning: normally the `previousData` is same instance as `currentData` because our `SDAnimatedImage` class share the same `coder` instance internally. But there may be a race condition, that later retrived `currentData` is already been updated and it's not the same instance as `previousData`.
            // And for protocol extensible design, we should not assume `SDAnimatedImage` protocol implementations always share same instance. So both of two reasons, we need that `rangeOfData` check.
            if ([currentData isEqualToData:previousData]) {
                // If current data is the same data (or instance) as previous data
                self.isProgressive = YES;
            } else if (currentData.length > previousData.length) {
                // If current data is appended by previous data, use `NSDataSearchAnchored`
                NSRange range = [currentData rangeOfData:previousData options:NSDataSearchAnchored range:NSMakeRange(0, previousData.length)];
                if (range.location == 0) {
                    // Contains hole previous data and they start with the same beginning
                    self.isProgressive = YES;
                }
            }
        } else {
            // Previous image is not progressive, so start progressive rendering
            self.isProgressive = YES;
        }
    }
}

#if SD_MAC
- (void)displayDidRefresh:(CVDisplayLinkRef)displayLink duration:(NSTimeInterval)duration
#else
- (void)displayDidRefresh:(CADisplayLink *)displayLink
#endif
{
    // If for some reason a wild call makes it through when we shouldn't be animating, bail.
    // Early return!
    if (!self.shouldAnimate) {
        return;
    }
    
#if SD_UIKIT
    NSTimeInterval duration = displayLink.duration * displayLink.frameInterval;

Review comment (suggestion):
The reason to change this ? We need to check that the current data contains hole previous data, not a subsequence of previous data. So that `range.length == previousData.length` is necessary.

Code after:
{
    self.isProgressive = NO;
    if (!self.shouldIncrementalLoad) {
        // Early return
        return;
    }
    if ([image conformsToProtocol:@protocol(SDAnimatedImage)] && image.sd_isIncremental) {
        UIImage *previousImage = self.image;
        if ([previousImage conformsToProtocol:@protocol(SDAnimatedImage)] && previousImage.sd_isIncremental) {
            NSData *previousData = [((UIImage<SDAnimatedImage> *)previousImage) animatedImageData];
            NSData *currentData = [((UIImage<SDAnimatedImage> *)image) animatedImageData];
            // Check whether to use progressive rendering or not
            if (!previousData || !currentData) {
                // Early return
                return;
            }
            
            // Warning: normally the `previousData` is same instance as `currentData` because our `SDAnimatedImage` class share the same `coder` instance internally. But there may be a race condition, that later retrived `currentData` is already been updated and it's not the same instance as `previousData`.
            // And for protocol extensible design, we should not assume `SDAnimatedImage` protocol implementations always share same instance. So both of two reasons, we need that `rangeOfData` check.
            if ([currentData isEqualToData:previousData]) {
                // If current data is the same data (or instance) as previous data
                self.isProgressive = YES;
            } else if (currentData.length > previousData.length) {
                // If current data is appended by previous data, use `NSDataSearchAnchored`, search is limited to start of currentData
                NSRange range = [currentData rangeOfData:previousData options:NSDataSearchAnchored range:NSMakeRange(0, previousData.length)];
                if (range.location != NSNotFound) {
                    // Contains hole previous data and they start with the same beginning
                    self.isProgressive = YES;
                }
            }
        } else {
            // Previous image is not progressive, so start progressive rendering
            self.isProgressive = YES;
        }
    }
}

#if SD_MAC
- (void)displayDidRefresh:(CVDisplayLinkRef)displayLink duration:(NSTimeInterval)duration
#else
- (void)displayDidRefresh:(CADisplayLink *)displayLink
#endif
{
    // If for some reason a wild call makes it through when we shouldn't be animating, bail.
    // Early return!
    if (!self.shouldAnimate) {
        return;
    }
    
#if SD_UIKIT
    NSTimeInterval duration = displayLink.duration * displayLink.frameInterval;