iOS SDWebImage源码解读

SDWebImage是在iOS开发中被广泛使用的一个第三方开源框架,对于网络图片的请求使用非常方便。

一、总体概述

它实现的大致思路是:首先根据LRU去缓存中查找,如果缓存中没有,去磁盘中查找,找到了,直接读取image,然后展示;如果没找到,将从网络上下载,下载完成后,拿到image展示,并添加到缓存中。如下图:

SDWebImage时序

总体类图如下:

SDWebImage类图

二、核心代码解读:

SDWebImageManager类核心方法解读:- (id)loadImageWithURL:(nullableNSURL*)url options:(SDWebImageOptions)options progress:(nullableSDWebImageDownloaderProgressBlock)progressBlock completed:(nullableSDInternalCompletionBlock)completedBlock;步骤:

1、判断url是否为无效链接;

2、创建管理队列SDWebImageCombinedOperation,并加入队列数组

3、从缓存SDImageCache(包括磁盘)中读取;

4、根据读取缓存结果,判断是否需要从网络下载;

5、通过SDWebImageDownloader从网络中下载image,下载成功,写到缓存;

6、回调,移除管理队列数组。

具体代码如下:


- (id)loadImageWithURL:(nullableNSURL*)url

                                     options:(SDWebImageOptions)options

                                    progress:(nullableSDWebImageDownloaderProgressBlock)progressBlock

                                   completed:(nullableSDInternalCompletionBlock)completedBlock {

    // Invoking this method without a completedBlock is pointless

    NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");

    // Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, Xcode won't

    // throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString.

    if ([url isKindOfClass:NSString.class]) {

        url = [NSURLURLWithString:(NSString*)url];

    }

    // Prevents app crashing on argument type error like sending NSNull instead of NSURL

    if (![url isKindOfClass:NSURL.class]) {

        url =nil;

    }

    ///管理队列,包含读写缓存缓存的队列,下载的队列

    SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];

    operation.manager=self;

    ///判断下载url是否是无效链接

    BOOLisFailedUrl =NO;

    if(url) {

        @synchronized (self.failedURLs) {

            isFailedUrl = [self.failedURLscontainsObject:url];

        }

    }

    if(url.absoluteString.length==0|| (!(options &SDWebImageRetryFailed) && isFailedUrl)) {

        [selfcallCompletionBlockForOperation:operationcompletion:completedBlockerror:[NSErrorerrorWithDomain:NSURLErrorDomaincode:NSURLErrorFileDoesNotExistuserInfo:nil]url:url];

        returnoperation;

    }

    ///将operationd管理队列添加到数组保存

    @synchronized (self.runningOperations) {

        [self.runningOperationsaddObject:operation];

    }

    ///将url转换为key,后面缓存相关需要用到

    NSString*key = [selfcacheKeyForURL:url];

    ///确定读取缓存策略

    SDImageCacheOptionscacheOptions =0;

    ///异步读取

    if (options & SDWebImageQueryDataWhenInMemory) cacheOptions |= SDImageCacheQueryDataWhenInMemory;

    ///同步读取

    if (options & SDWebImageQueryDiskSync) cacheOptions |= SDImageCacheQueryDiskSync;


    __weakSDWebImageCombinedOperation*weakOperation = operation;

    ///读取缓存中的数据,并返回读取缓存的队列

    operation.cacheOperation= [self.imageCache queryCacheOperationForKey:key options:cacheOptions done:^(UIImage*cachedImage,NSData*cachedData,SDImageCacheType cacheType) {

        __strong__typeof(weakOperation) strongOperation = weakOperation;

        if(!strongOperation || strongOperation.isCancelled) {

            [selfsafelyRemoveOperationFromRunning:strongOperation];

            return;

        }


        // Check whether we should download image from network是否需要从网络下载图片

        BOOLshouldDownload = (!(options &SDWebImageFromCacheOnly))

            && (!cachedImage || options &SDWebImageRefreshCached)

            && (![self.delegaterespondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegateimageManager:selfshouldDownloadImageForURL:url]);

        if(shouldDownload) {///需要从网络中下载

            if(cachedImage && options &SDWebImageRefreshCached) {

                // If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image

                // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.

                [selfcallCompletionBlockForOperation:strongOperationcompletion:completedBlockimage:cachedImagedata:cachedDataerror:nilcacheType:cacheTypefinished:YESurl:url];

            }

            // download if no image or requested to refresh anyway, and download allowed by delegate

            ///下载策略

            SDWebImageDownloaderOptionsdownloaderOptions =0;

            if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;

            if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;

            if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;

            if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;

            if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;

            if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;

            if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;

            if (options & SDWebImageScaleDownLargeImages) downloaderOptions |= SDWebImageDownloaderScaleDownLargeImages;


            if(cachedImage && options &SDWebImageRefreshCached) {

                // force progressive off if image already cached but forced refreshing

                downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;

                // ignore image read from NSURLCache if image if cached but force refreshing

                downloaderOptions |=SDWebImageDownloaderIgnoreCachedResponse;

            }


            // `SDWebImageCombinedOperation` -> `SDWebImageDownloadToken` -> `downloadOperationCancelToken`, which is a `SDCallbacksDictionary` and retain the completed block below, so we need weak-strong again to avoid retain cycle

            __weaktypeof(strongOperation) weakSubOperation = strongOperation;

            ///下载图片,并返回下载队列信息

            strongOperation.downloadToken= [self.imageDownloaderdownloadImageWithURL:urloptions:downloaderOptionsprogress:progressBlockcompleted:^(UIImage*downloadedImage,NSData*downloadedData,NSError*error,BOOLfinished) {

                __strongtypeof(weakSubOperation) strongSubOperation = weakSubOperation;

                if(!strongSubOperation || strongSubOperation.isCancelled) {///下载队列被取消,不做处理

                    // Do nothing if the operation was cancelled

                    // See #699 for more details

                    // if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data

                }elseif(error) {///下载失败

                    [selfcallCompletionBlockForOperation:strongSubOperationcompletion:completedBlockerror:errorurl:url];

                    BOOLshouldBlockFailedURL;

                    // Check whether we should block failed url

                    if([self.delegaterespondsToSelector:@selector(imageManager:shouldBlockFailedURL:withError:)]) {

                        shouldBlockFailedURL = [self.delegateimageManager:selfshouldBlockFailedURL:urlwithError:error];

                    }else{

                        shouldBlockFailedURL = (  error.code!=NSURLErrorNotConnectedToInternet

                                                && error.code!=NSURLErrorCancelled

                                                && error.code!=NSURLErrorTimedOut

                                                && error.code!=NSURLErrorInternationalRoamingOff

                                                && error.code!=NSURLErrorDataNotAllowed

                                                && error.code!=NSURLErrorCannotFindHost

                                                && error.code!=NSURLErrorCannotConnectToHost

                                                && error.code!=NSURLErrorNetworkConnectionLost);

                    }

                    ///添加到下载失败数组中

                    if(shouldBlockFailedURL) {

                        @synchronized(self.failedURLs) {

                            [self.failedURLsaddObject:url];

                        }

                    }

                }

                else{///下载成功,且队列没被取消

                    if((options &SDWebImageRetryFailed)) {

                        @synchronized(self.failedURLs) {

                            [self.failedURLsremoveObject:url];

                        }

                    }


                    BOOLcacheOnDisk = !(options &SDWebImageCacheMemoryOnly);


                    // We've done the scale process in SDWebImageDownloader with the shared manager, this is used for custom manager and avoid extra scale.

                    if(self!= [SDWebImageManagersharedManager] &&self.cacheKeyFilter&& downloadedImage) {

                        downloadedImage = [selfscaledImageForKey:keyimage:downloadedImage];

                    }

                    if(options &SDWebImageRefreshCached&& cachedImage && !downloadedImage) {

                        // Image refresh hit the NSURLCache cache, do not call the completion block

                    }elseif(downloadedImage && (!downloadedImage.images|| (options &SDWebImageTransformAnimatedImage)) && [self.delegaterespondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {

                        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{

                            UIImage*transformedImage = [self.delegateimageManager:selftransformDownloadedImage:downloadedImagewithURL:url];

                            if(transformedImage && finished) {

                                BOOLimageWasTransformed = ![transformedImageisEqual:downloadedImage];

                                NSData*cacheData;

                                // pass nil if the image was transformed, so we can recalculate the data from the image

                                if(self.cacheSerializer) {

                                    cacheData =self.cacheSerializer(transformedImage, (imageWasTransformed ?nil: downloadedData), url);

                                }else{

                                    cacheData = (imageWasTransformed ?nil: downloadedData);

                                }

                                [self.imageCachestoreImage:transformedImageimageData:cacheDataforKey:keytoDisk:cacheOnDiskcompletion:nil];

                            }


                            [selfcallCompletionBlockForOperation:strongSubOperationcompletion:completedBlockimage:transformedImagedata:downloadedDataerror:nilcacheType:SDImageCacheTypeNonefinished:finishedurl:url];

                        });

                    }else{

                        if(downloadedImage && finished) {///根据不同方式写缓存

                            if(self.cacheSerializer) {

                                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0), ^{

                                    NSData*cacheData =self.cacheSerializer(downloadedImage, downloadedData, url);

                                    [self.imageCachestoreImage:downloadedImageimageData:cacheDataforKey:keytoDisk:cacheOnDiskcompletion:nil];

                                });

                            }else{

                                [self.imageCachestoreImage:downloadedImageimageData:downloadedDataforKey:keytoDisk:cacheOnDiskcompletion:nil];

                            }

                        }

                        ///回调

                        [selfcallCompletionBlockForOperation:strongSubOperationcompletion:completedBlockimage:downloadedImagedata:downloadedDataerror:nilcacheType:SDImageCacheTypeNonefinished:finishedurl:url];

                    }

                }

                if(finished) {

                    [selfsafelyRemoveOperationFromRunning:strongSubOperation];

                }

            }];

        }elseif(cachedImage) {///存在缓存图片

            ///回调

            [selfcallCompletionBlockForOperation:strongOperationcompletion:completedBlockimage:cachedImagedata:cachedDataerror:nilcacheType:cacheTypefinished:YESurl:url];

            [selfsafelyRemoveOperationFromRunning:strongOperation];

        }else {///缓存中不存在且不让下载

            ///回调

            // Image not in cache and download disallowed by delegate

            [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];

            [selfsafelyRemoveOperationFromRunning:strongOperation];

        }

    }];

    returnoperation;

}


SDWebImageDownloader的核心方法解读- (nullableSDWebImageDownloadToken*)downloadImageWithURL:(nullableNSURL*)url options:(SDWebImageDownloaderOptions)options progress:(nullableSDWebImageDownloaderProgressBlock)progressBlock completed:(nullableSDWebImageDownloaderCompletedBlock)completedBlock;步骤:

1、根据url创建对应的网络请求NSURLRequest;

2、根据NSURLRequest等创建下载管理队列SDWebImageDownloaderOperation;

3、创建下载token,下载唯一标志结构体,包含下载队列,下载相关回调,urle等相关信息;

4、将SDWebImageDownloaderOperation添加到downloadQueue中,根据优先级开始下载。

注意:下载相关回调在SDWebImageDownloaderOperation类中处理。

具体代码如下:


- (nullableSDWebImageDownloadToken*)downloadImageWithURL:(nullableNSURL*)url

                                                   options:(SDWebImageDownloaderOptions)options

                                                  progress:(nullableSDWebImageDownloaderProgressBlock)progressBlock

                                                 completed:(nullableSDWebImageDownloaderCompletedBlock)completedBlock {

    __weak SDWebImageDownloader *wself = self;

    return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{

        __strong__typeof(wself) sself = wself;

        ///设置超时时间,默认15秒

        NSTimeIntervaltimeoutInterval = sself.downloadTimeout;

        if(timeoutInterval ==0.0) {

            timeoutInterval =15.0;

        }

        // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise

        NSURLRequestCachePolicy cachePolicy = options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;

        ///创建下载请求

        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url

                                                                    cachePolicy:cachePolicy

                                                                timeoutInterval:timeoutInterval];


        request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);

        request.HTTPShouldUsePipelining = YES;

        if(sself.headersFilter) {

            request.allHTTPHeaderFields= sself.headersFilter(url, [sselfallHTTPHeaderFields]);

        }

        else{

            request.allHTTPHeaderFields= [sselfallHTTPHeaderFields];

        }

        ///下载管理队列

        SDWebImageDownloaderOperation*operation = [[sself.operationClassalloc]initWithRequest:requestinSession:sself.sessionoptions:options];

        operation.shouldDecompressImages = sself.shouldDecompressImages;

        ///是否需要账号密码

        if(sself.urlCredential) {

            operation.credential= sself.urlCredential;

        }elseif(sself.username&& sself.password) {

            operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];

        }

        ///设置优先级

        if (options & SDWebImageDownloaderHighPriority) {

            operation.queuePriority = NSOperationQueuePriorityHigh;

        }else if (options & SDWebImageDownloaderLowPriority) {

            operation.queuePriority = NSOperationQueuePriorityLow;

        }


        if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {

            // Emulate LIFO execution order by systematically adding new operations as last operation's dependency

            [sself.lastAddedOperationaddDependency:operation];

            sself.lastAddedOperation= operation;

        }

        returnoperation;

    }];

}

///缓存相关回调

- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock

                                           completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock

                                                   forURL:(nullableNSURL*)url

                                           createCallback:(SDWebImageDownloaderOperation*(^)(void))createCallback {

    // The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data.

    if(url ==nil) {

        if(completedBlock !=nil) {

            completedBlock(nil,nil,nil,NO);

        }

        returnnil;

    }


    LOCK(self.operationsLock);

    ///下载队列

    SDWebImageDownloaderOperation *operation = [self.URLOperations objectForKey:url];

    if(!operation) {

        operation = createCallback();

        __weaktypeof(self) wself =self;

        ///下载完成后的处理

        operation.completionBlock= ^{

            __strongtypeof(wself) sself = wself;

            if(!sself) {

                return;

            }

            LOCK(sself.operationsLock);

            [sself.URLOperationsremoveObjectForKey:url];

            UNLOCK(sself.operationsLock);

        };

        ///保存队列

        [self.URLOperationssetObject:operationforKey:url];

        // Add operation to operation queue only after all configuration done according to Apple's doc.

        // `addOperation:` does not synchronously execute the `operation.completionBlock` so this will not cause deadlock.

        ///开始下载

        [self.downloadQueueaddOperation:operation];

    }

    UNLOCK(self.operationsLock);

    ///下载相关回调

    iddownloadOperationCancelToken = [operationaddHandlersForProgress:progressBlockcompleted:completedBlock];

    ///下载token,唯一标志,包含下载队列,下载相关回调,urle等相关信息

    SDWebImageDownloadToken *token = [SDWebImageDownloadToken new];

    token.downloadOperation= operation;

    token.url= url;

    token.downloadOperationCancelToken= downloadOperationCancelToken;

    returntoken;

}


SDImageCache核心方法解读- (nullableNSOperation*)queryCacheOperationForKey:(nullableNSString*)key options:(SDImageCacheOptions)options done:(nullableSDCacheQueryCompletedBlock)doneBlock :

1、从内存中读;

2、根据读取结果和策略SDImageCacheOptions判断是否需要从磁盘中读取;

3、创建读取管理队列NSOperation,返回给manager;

4、创建从磁盘读取任务,根据读取策略,同步读取或者一步读取。

具体代码如下:


- (nullableNSOperation*)queryCacheOperationForKey:(nullableNSString*)key options:(SDImageCacheOptions)options done:(nullableSDCacheQueryCompletedBlock)doneBlock {

    if(!key) {

        if(doneBlock) {

            doneBlock(nil,nil,SDImageCacheTypeNone);

        }

        returnnil;

    }


    // First check the in-memory cache...从内存中读取

    UIImage *image = [self imageFromMemoryCacheForKey:key];

    BOOLshouldQueryMemoryOnly = (image && !(options &SDImageCacheQueryDataWhenInMemory));

    if(shouldQueryMemoryOnly) {///只从内存中读

        if(doneBlock) {

            doneBlock(image,nil,SDImageCacheTypeMemory);

        }

        returnnil;

    }

    ///创建读取队列

    NSOperation*operation = [NSOperationnew];

    void(^queryDiskBlock)(void) =  ^{///从硬盘中读

        if(operation.isCancelled) {

            // do not call the completion if cancelled

            return;

        }


        @autoreleasepool {

            NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];

            UIImage*diskImage;

            SDImageCacheTypecacheType =SDImageCacheTypeDisk;

            if(image) {

                // the image is from in-memory cache

                diskImage = image;

                cacheType =SDImageCacheTypeMemory;

            }elseif(diskData) {

                // decode image data only if in-memory cache missed

                diskImage = [selfdiskImageForKey:keydata:diskData];

                if(diskImage &&self.config.shouldCacheImagesInMemory) {

                    NSUIntegercost =SDCacheCostForImage(diskImage);

                    [self.memCachesetObject:diskImageforKey:keycost:cost];

                }

            }


            if(doneBlock) {

                if(options &SDImageCacheQueryDiskSync) {

                    doneBlock(diskImage, diskData, cacheType);

                }else{

                    dispatch_async(dispatch_get_main_queue(), ^{

                        doneBlock(diskImage, diskData, cacheType);

                    });

                }

            }

        }

    };


    if (options & SDImageCacheQueryDiskSync) {

        queryDiskBlock();///同步从硬盘中读取

    }else{

        ///异步从硬盘中读取

        dispatch_async(self.ioQueue, queryDiskBlock);

    }


    returnoperation;

}

图片解码SDWebImageImageIOCoder的核心方法:- (nullable UIImage *)sd_decompressedAndScaledDownImageWithImage:(nullable UIImage *)image

- (nullableUIImage*)sd_decompressedAndScaledDownImageWithImage:(nullableUIImage*)image {

    if(![[self class] shouldDecodeImage:image]) {

        returnimage;

    }


    if(![[self class] shouldScaleDownImage:image]) {

        return [self sd_decompressedImageWithImage:image];

    }


    CGContextRef destContext;


    // autorelease the bitmap context and all vars to help system to free memory when there are memory warning.

    // on iOS7, do not forget to call [[SDImageCache sharedImageCache] clearMemory];

    @autoreleasepool {

        CGImageRef sourceImageRef = image.CGImage;


        CGSize sourceResolution =CGSizeZero;

        sourceResolution.width=CGImageGetWidth(sourceImageRef);

        sourceResolution.height=CGImageGetHeight(sourceImageRef);

        float sourceTotalPixels = sourceResolution.width* sourceResolution.height;

        // Determine the scale ratio to apply to the input image

        // that results in an output image of the defined size.

        // see kDestImageSizeMB, and how it relates to destTotalPixels.

        float imageScale =kDestTotalPixels/ sourceTotalPixels;

        CGSize destResolution =CGSizeZero;

        destResolution.width= (int)(sourceResolution.width*imageScale);

        destResolution.height= (int)(sourceResolution.height*imageScale);


        // current color space

        CGColorSpaceRef colorspaceRef = [[self class] colorSpaceForImageRef:sourceImageRef];


        // kCGImageAlphaNone is not supported in CGBitmapContextCreate.

        // Since the original image here has no alpha info, use kCGImageAlphaNoneSkipLast

        // to create bitmap graphics contexts without alpha info.

        destContext =CGBitmapContextCreate(NULL,

                                            destResolution.width,

                                            destResolution.height,

                                            kBitsPerComponent,

                                            0,

                                            colorspaceRef,

                                            kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast);


        if(destContext ==NULL) {

            returnimage;

        }

        CGContextSetInterpolationQuality(destContext, kCGInterpolationHigh);


        // Now define the size of the rectangle to be used for the

        // incremental blits from the input image to the output image.

        // we use a source tile width equal to the width of the source

        // image due to the way that iOS retrieves image data from disk.

        // iOS must decode an image from disk in full width 'bands', even

        // if current graphics context is clipped to a subrect within that

        // band. Therefore we fully utilize all of the pixel data that results

        // from a decoding opertion by achnoring our tile size to the full

        // width of the input image.

        CGRect sourceTile =CGRectZero;

        sourceTile.size.width= sourceResolution.width;

        // The source tile height is dynamic. Since we specified the size

        // of the source tile in MB, see how many rows of pixels high it

        // can be given the input image width.

        sourceTile.size.height= (int)(kTileTotalPixels/ sourceTile.size.width);

        sourceTile.origin.x=0.0f;

        // The output tile is the same proportions as the input tile, but

        // scaled to image scale.

        CGRect destTile;

        destTile.size.width= destResolution.width;

        destTile.size.height= sourceTile.size.height* imageScale;

        destTile.origin.x=0.0f;

        // The source seem overlap is proportionate to the destination seem overlap.

        // this is the amount of pixels to overlap each tile as we assemble the ouput image.

        float sourceSeemOverlap = (int)((kDestSeemOverlap/destResolution.height)*sourceResolution.height);

        CGImageRef sourceTileImageRef;

        // calculate the number of read/write operations required to assemble the

        // output image.

        intiterations = (int)( sourceResolution.height/ sourceTile.size.height);

        // If tile height doesn't divide the image height evenly, add another iteration

        // to account for the remaining pixels.

        intremainder = (int)sourceResolution.height% (int)sourceTile.size.height;

        if(remainder) {

            iterations++;

        }

        // Add seem overlaps to the tiles, but save the original tile height for y coordinate calculations.

        float sourceTileHeightMinusOverlap = sourceTile.size.height;

        sourceTile.size.height+= sourceSeemOverlap;

        destTile.size.height+=kDestSeemOverlap;

        for(inty =0; y < iterations; ++y ) {

            @autoreleasepool {

                sourceTile.origin.y= y * sourceTileHeightMinusOverlap + sourceSeemOverlap;

                destTile.origin.y= destResolution.height- (( y +1) * sourceTileHeightMinusOverlap * imageScale +kDestSeemOverlap);

                sourceTileImageRef =CGImageCreateWithImageInRect( sourceImageRef, sourceTile );

                if( y == iterations -1&& remainder ) {

                    float dify = destTile.size.height;

                    destTile.size.height=CGImageGetHeight( sourceTileImageRef ) * imageScale;

                    dify -= destTile.size.height;

                    destTile.origin.y+= dify;

                }

                CGContextDrawImage( destContext, destTile, sourceTileImageRef );

                CGImageRelease( sourceTileImageRef );

            }

        }


        CGImageRef destImageRef =CGBitmapContextCreateImage(destContext);

        CGContextRelease(destContext);

        if(destImageRef ==NULL) {

            returnimage;

        }

        UIImage *destImage = [[UIImage alloc] initWithCGImage:destImageRef scale:image.scale orientation:image.imageOrientation];

        CGImageRelease(destImageRef);

        if(destImage ==nil) {

            return image;

        }

        return destImage;

    }

}


三、线程模型:

总体讲,SDWebImage有四个后台队列,一个是下载队列SDWebImageDownloaderOperation,一个是读取缓存队列(从磁盘中异步读取在后台,同步读取在主队列),一个是下载完成后需要序列化后写缓存的全局队列(其他的在下载回调主队列写),最后一个是编解码的队列。具体看代码:

1、下载队列:

SDWebImageDownloaderOperation继承NSOperation

SDWebImageDownloaderOperation
下载队列
初始化

2、读取缓存的队列:

后台窜行队列
根据读取策略同步异步

3、写缓存队列:

写缓存队列

4、图片解码队列:

解码队列
解码窜行队列

以上,欢迎指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342