SDWebImage的主流程

article/2025/10/8 2:16:27

SDWebImage提供了UIImageView的分类来实现从网络端下载数据并缓存到内存和磁盘。非常的流行,现在就主流程分析下.

主要的学习链接 :SDWebImage源码解读 - 简书
 

SDWebImage有如下特点:

  • 提供了UIImageView和UIButton的分类。以支持加载网络图片并缓存。
  • 一个异步的图片下载器
  • 提供异步的内存和磁盘缓存,并自动处理缓存过期和缓存大小设置。
  • 后台图片解压缩处理。
  • 确保同一个URL不会被下载多次。
  • 确保主线程永远不会阻塞。

一.储备知识

SDWebImage中每一个下载任务都是一个SDWebImageDownloaderOperation,而SDWebImageDownloaderOperation又是继承自NSOperation,所以每一个下载任务对应一个NSOperation。在SDWebImage中使用SDWebImageDownloader来管理
多个下载任务,在SDWebImageDownloader中有一个downloadedQueue这个属性,这个属性是NSOperationQueue类型的,也就是用NSOperationQueue来管理NSOperation

NSOperation和NSOperationQueue

NSOperation是一个抽象类,用来表示与单个任务相关联的代码和数据。

我们可以把一个NSOperation对象加入到一个operation queue中,让这个operation queue去决定什么时候执行这个operation。当使用operation queue去管理operation时,轮到某个operation执行时实际是去执行这个operation的start方法,所以我们一个operation对象实际要执行的任务应该放在start方法里面。如果我们不想使用operation queue,也可以通过手动调用NSOperation的start方法来执行任务。

只要当一个operation对象的所有依赖都执行完成的时候,其才可以变成熟ready状态,然后才可以被执行。如果一个operation没有添加依赖,直接加入了operation queue中,那么就会按照加入队列的先后顺序,当这个operation的前一个operation执行完成以后,其状态才会变成ready,才能被执行。

NSOperation对象有下列四个比较重要的状态:

  • isCancelled
  • isExecuting
  • isFinished
  • isReady
    其中isExecutingisFinishedisReady这三种状态相当于是operation对象的生命周期:

框架的主要类和一次图片加载的主要流程

框架的主要类

从上图也可以看出,整个框架主要分为管理者 , 图片的下载,图片的缓存,和处理解压图片相关的类。

下载

  • SDWebImageDownloader:实际的下载功能和配置提供者,使用了单例的设计模式。
  • SDWebImageDownloaderOperation:继承自NSOperation,是一个异步的NSOperation,封装了NSURLSession进行实际的下载任务。

缓存处理

  • SDImageCache:继承自NSCache,实际处理内存cache和磁盘cache。
  • SDImageCacheConfig:缓存处理的配置。
  • SDWebImageCoder:定义了编码解码的协议,从而可以实现面向协议编程。

功能类

  • SDWebImageManager:宏观的从整体上管理整个框架的类。
  • SDWebImagePrefetcher:图片的预加载管理。

图片的编码解码处理

  • SDWebImageCodersManager:实际的编码解码功能处理,使用了单例模式。
  • 在子线程预解码图片生成位图, 在需要显示的时候就不需要在主线程解码了
  • 主流图片加载库所使用的预解码究竟干了什么

Category

  • 类别用来为UIView和UIImageView等”添加”属性来存储必要的信息,同时暴露出接口,进行实际的操作。

一次图片加载的主要流程

针对上图中一次图片加载的主要流程,每一步做介绍:

  • 1.SDWebImage为UIImagView创建了一个分类UIImageView (WebCache),然后UIImageView对象可以调用这个分类的方法来下载图片:
    [imageView sd_setImageWithURL:[NSURL URLWithString:@""]];
  • 2.UIImageView (WebCache)- (void)sd_setImageWithURL:(nullable NSURL *)url方法实际调用了UIView (WebCache)的下列方法:
- (void)sd_internalSetImageWithURL:(nullable NSURL *)urlplaceholderImage:(nullable UIImage *)placeholderoptions:(SDWebImageOptions)optionsoperationKey:(nullable NSString *)operationKeysetImageBlock:(nullable SDSetImageBlock)setImageBlockprogress:(nullable SDWebImageDownloaderProgressBlock)progressBlockcompleted:(nullable SDExternalCompletionBlock)completedBlock;
  • 3.UIView (WebCache)的上述方法在实现时会创建一个SDWebImageManager的实例对象,然后调用其下列方法来加载图片:
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)urloptions:(SDWebImageOptions)optionsprogress:(nullable SDWebImageDownloaderProgressBlock)progressBlockcompleted:(nullable SDInternalCompletionBlock)completedBlock;
  • 4.在SDWebImageManager对象的上述方法里,首先会查询在缓存中有没有这个图片,然后根据各种option的判断决定是否要从网络端下载。查询缓存中有没有是通过调用SDImageCache对象的实例方法来实现的:
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock;
  • 5.返回缓存查询的结果
  • 6.如果需要下载图片,那么调用SDWebImageDownloader对象的下列方法进行下载:
- (SDWebImageDownloadToken *)downloadImageWithURL:(NSURL *)urloptions:(SDWebImageDownloaderOptions)optionsprogress:(SDWebImageDownloaderProgressBlock)progressBlockcompleted:(SDWebImageDownloaderCompletedBlock)completedBlock;
  • 7.获取从网络端下载的图片。
  • 8.判断是否要将下载的图片进行缓存,如果需要,则缓存。
  • 9.把通过SDWebImageManager对象获取的图片显示在UIImageView上。

源码分析

这一部分我们进行详细的源码分析。

1. UIImageVIew的类别

// 1.1 UIImageView类别给出的接口
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder {[self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
}// 1.2 调到了这里,这里有调到UIVIew的类别
- (void)sd_setImageWithURL:(nullable NSURL *)urlplaceholderImage:(nullable UIImage *)placeholderoptions:(SDWebImageOptions)optionsprogress:(SDWebImageDownloaderProgressBlock)progressBlockcompleted:(SDExternalCompletionBlock)completedBlock {[self sd_internalSetImageWithURL:urlplaceholderImage:placeholderoptions:optionsoperationKey:nilsetImageBlock:nilprogress:progressBlockcompleted:completedBlock];
}

2. 调到了UIView的类别

// 2.UIView的类别
- (void)sd_internalSetImageWithURL:(nullable NSURL *)urlplaceholderImage:(nullable UIImage *)placeholderoptions:(SDWebImageOptions)optionsoperationKey:(nullable NSString *)operationKeysetImageBlock:(nullable SDSetImageBlock)setImageBlockprogress:(nullable SDWebImageDownloaderProgressBlock)progressBlockcompleted:(nullable SDExternalCompletionBlock)completedBlock {NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);[self sd_cancelImageLoadOperationWithKey:validOperationKey];objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);if (!(options & SDWebImageDelayPlaceholder)) {dispatch_main_async_safe(^{[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];});}if (url) {// check if activityView is enabled or notif ([self sd_showActivityIndicatorView]) {[self sd_addActivityIndicator];}__weak __typeof(self)wself = self;// 找manager问图片,然后处理找到的情况id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {// 假设已经找到了__strong __typeof (wself) sself = wself;[sself sd_removeActivityIndicator];if (!sself) {return;}dispatch_main_async_safe(^{if (!sself) {return;}// 找到了就回调if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {completedBlock(image, error, cacheType, url);return;} else if (image) {// 找了图片,但是completedBlock为nil,用默认的回调[sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock];[sself sd_setNeedsLayout];} else {// 没找到图片,还是用占位图if ((options & SDWebImageDelayPlaceholder)) {[sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];[sself sd_setNeedsLayout];}}if (completedBlock && finished) {completedBlock(image, error, cacheType, url);}});}];[self sd_setImageLoadOperation:operation forKey:validOperationKey];} else {dispatch_main_async_safe(^{[self sd_removeActivityIndicator];if (completedBlock) {completedBlock(nil, error, SDImageCacheTypeNone, url);}});}
}


前面的调用比较简单,现在到了第3步SDWebImageManager类的loadImageWithURL:方法看起:

/// 3.在这里加载图片,核心的核心. 检查内存,硬盘,网络下载
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)urloptions:(SDWebImageOptions)optionsprogress:(nullable SDWebImageDownloaderProgressBlock)progressBlockcompleted:(nullable SDInternalCompletionBlock)completedBlock {// Invoking this method without a completedBlock is pointlessNSAssert(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 = [NSURL URLWithString:(NSString *)url];}// Prevents app crashing on argument type error like sending NSNull instead of NSURLif (![url isKindOfClass:NSURL.class]) {url = nil;}__block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];__weak SDWebImageCombinedOperation *weakOperation = operation;// 检查是否有过失败记录了,如果下载失败过,就不重复尝试了BOOL isFailedUrl = NO;if (url) {@synchronized (self.failedURLs) {isFailedUrl = [self.failedURLs containsObject:url];}}if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {[self callCompletionBlockForOperation:operation 
completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url];return operation;}// 把operation添加进入调度数组@synchronized (self.runningOperations) {[self.runningOperations addObject:operation];}NSString *key = [self cacheKeyForURL:url];// 生成一个查找缓存队列的NSOperation , operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key 
done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {// 这里是查询结果,可能找到了图片,也可能没找到if (operation.isCancelled) {[self safelyRemoveOperationFromRunning:operation];return;}// 图片没有找到,去网络下载if (....) {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.[self callCompletionBlockForOperation:weakOperation 
completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];}.......// 图片没有找到,去网络下载SDWebImageDownloadToken *subOperationToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock 
completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {// 下载结果的回调__strong __typeof(weakOperation) strongOperation = weakOperation;// 下载取消了if (!strongOperation || strongOperation.isCancelled) {// Do nothing if the operation was cancelled// See #699 for more details} else if (error) {// 下载出错了,把这个URL存入下载失败的URL中,以后不重复尝试了[self callCompletionBlockForOperation:strongOperation 
completion:completedBlock error:error url:url];if (   ....  ) {@synchronized (self.failedURLs) {[self.failedURLs addObject:url];}}}else {// 下载成功,下载失败的URL中移除掉if ((options & SDWebImageRetryFailed)) {@synchronized (self.failedURLs) {[self.failedURLs removeObject:url];}}BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) {// Image refresh hit the NSURLCache cache, do not call the completion block} else if (.....) {dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];if (transformedImage && finished) {BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];// pass nil if the image was transformed, so we can recalculate the data from the image[self.imageCache storeImage:transformedImage imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk completion:nil];}[self callCompletionBlockForOperation:strongOperation 
completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];});} else {// 下载成功,存入内存和硬盘if (downloadedImage && finished) {[self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil];}[self callCompletionBlockForOperation:strongOperation 
completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];}}// 下载完成了,从活动operation中移除这个if (finished) {[self safelyRemoveOperationFromRunning:strongOperation];}}];operation.cancelBlock = ^{[self.imageDownloader cancel:subOperationToken];__strong __typeof(weakOperation) strongOperation = weakOperation;[self safelyRemoveOperationFromRunning:strongOperation];};} else if (cachedImage) {// 图片在内存或者硬盘中找到了,调用回调__strong __typeof(weakOperation) strongOperation = weakOperation;[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];[self safelyRemoveOperationFromRunning:operation];} else {// Image not in cache and download disallowed by delegate__strong __typeof(weakOperation) strongOperation = weakOperation;[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];[self safelyRemoveOperationFromRunning:operation];}}];return operation;
}

总结一下SDWebImageManagerloadImageWithURL:所做的事情:

其实在loadImageWithURL:里面做了加载图片的完整流程。首先检查传入的NSURL的有效性。然后开始从缓存中查找是否有这个图片,得到查询结果之后再根据查询结果和设置的option判断是否需要进行下载操作,如果不需要下载操作那么就直接使用cache image进行下载回调。如果需要进行下载操作那么就开始下载,下载完成后按照设置的option将图片缓存到内存和磁盘,最后进行完成的回调。

然后我们看一下第4步 , 查询缓存的具体过程,也就是SDImageCache这个类的queryCacheOperationForKey:方法:
这里也是采用注释的方式

/// 4.SDImageCache重要的外界接口,根据key查找出图片,并回调回去,本类的核心方法
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key 
done:(nullable SDCacheQueryCompletedBlock)doneBlock {if (!key) {if (doneBlock) {doneBlock(nil, nil, SDImageCacheTypeNone);}return nil;}// First check the in-memory cache...  先从内存中找UIImage *image = [self imageFromMemoryCacheForKey:key];if (image) {NSData *diskData = nil;if ([image isGIF]) {diskData = [self diskImageDataBySearchingAllPathsForKey:key];}if (doneBlock) {doneBlock(image, diskData, SDImageCacheTypeMemory);}return nil;}// 在io线程,查找内存,如果在内存中找到了,缓存一份到cache中NSOperation *operation = [NSOperation new];dispatch_async(self.ioQueue, ^{if (operation.isCancelled) {// do not call the completion if cancelledreturn;}@autoreleasepool {NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];UIImage *diskImage = [self diskImageForKey:key];if (diskImage && self.config.shouldCacheImagesInMemory) {NSUInteger cost = SDCacheCostForImage(diskImage);[self.memCache setObject:diskImage forKey:key cost:cost];}if (doneBlock) {dispatch_async(dispatch_get_main_queue(), ^{doneBlock(diskImage, diskData, SDImageCacheTypeDisk);});}}});return operation;
}

总结一下queryCacheOperationForKey:方法所做的事情:

SDImageCache这个类是专门负责缓存相关的问题的,包括查询缓存和将图片进行缓存。SDImageCache使用了一个NSCache对象来进行内存缓存,磁盘缓存则是把图片数据存放在应用沙盒的 Caches/default/com.hackemist.SDWebImageCache.default/ ...具体的图片文件

首先查询内存缓存,内存缓存查询完了以后再判断是否需要查询磁盘缓存。如果查询内存缓存已经有了结果并且没有设置一定要查询磁盘缓存,那么就不查询磁盘缓存,否则就要查询磁盘缓存。内存缓存没有查询到图片,并且磁盘缓存查询到了图片,那么就要把这个内容缓存到内存缓存中。

图片的缓存查询完成后我们再来看一下第6步 , 下载操作,即SDWebImageDownloaderdownloadImageWithURL:方法

/// 6.真正的下载开始了,生成了下载的request和对应的operation
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(NSURL *)urloptions:(SDWebImageDownloaderOptions)optionsprogress:(SDWebImageDownloaderProgressBlock)progressBlockcompleted:(SDWebImageDownloaderCompletedBlock)completedBlock {__weak SDWebImageDownloader *wself = self;return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{// 生成一个下载的Request,添加进入下载队列中__strong __typeof (wself) sself = wself;NSTimeInterval timeoutInterval = sself.downloadTimeout;if (timeoutInterval == 0.0) {timeoutInterval = 15.0;}NSURLRequestCachePolicy cachePolicy = options & SDWebImageDownloaderUseNSURLCache ?NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:urlcachePolicy:cachePolicytimeoutInterval:timeoutInterval];request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);request.HTTPShouldUsePipelining = YES;if (sself.headersFilter) {request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]);}else {request.allHTTPHeaderFields = sself.HTTPHeaders;}SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];operation.shouldDecompressImages = sself.shouldDecompressImages;if (sself.urlCredential) {operation.credential = sself.urlCredential;} else if (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;}[sself.downloadQueue addOperation:operation];if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {[sself.lastAddedOperation addDependency:operation];sself.lastAddedOperation = operation;}return operation;}];
}

SDWebImageDownloader这个类是专门管理下载的,它有一个属性是downloadQueue,这是一个NSOperationQueue,每创建一个新的下载任务都把它加入到这个downloadQueue中,让downloadQueue去管理任务的开始,取消,结束。

上面的方法其实做的事情很简单,就是创建了一个下载图片的operation,然后把它加入到了downloadQueue中去。

下面的过程就是下载了  ,即SDWebImageDownloader类的addProgressCallback:方法:

/// 生成operation对应的token,然后把进度和完成的回调放到operation上
- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlockcompletedBlock:(SDWebImageDownloaderCompletedBlock)completedBlockforURL:(nullable NSURL *)urlcreateCallback:(SDWebImageDownloaderOperation *))createCallback {if (url == nil) {if (completedBlock != nil) {completedBlock(nil, nil, nil, NO);}return nil;}__block SDWebImageDownloadToken *token = nil;dispatch_barrier_sync(self.barrierQueue, ^{SDWebImageDownloaderOperation *operation = self.URLOperations[url];if (!operation) {operation = createCallback();self.URLOperations[url] = operation;__weak SDWebImageDownloaderOperation *woperation = operation;operation.completionBlock = ^{dispatch_barrier_sync(self.barrierQueue, ^{SDWebImageDownloaderOperation *soperation = woperation;if (!soperation) return;if (self.URLOperations[url] == soperation) {[self.URLOperations removeObjectForKey:url];};});};}id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];token = [SDWebImageDownloadToken new];token.url = url;token.downloadOperationCancelToken = downloadOperationCancelToken;});return token;
}

我们知道,NSOperation类的真正执行任务是在其start方法里面,那么我们看一下SDWebImageDownloaderOperationstart方法的具体实现:
代码比较长,我在关键部分加了注释

/// 这个SDWebImageDownloaderOperation任务启动了
- (void)start {@synchronized (self) {if (self.isCancelled) {self.finished = YES;[self reset];return;}#if SD_UIKIT//这一部分就是解决在后台仍然进行下载的问题Class UIApplicationClass = NSClassFromString(@"UIApplication");BOOL hasApplication = UIApplicationClass && [UIApplicationClass respondsToSelector:@selector(sharedApplication)];if (hasApplication && [self shouldContinueWhenAppEntersBackground]) {__weak __typeof__ (self) wself = self;UIApplication * app = [UIApplicationClass performSelector:@selector(sharedApplication)];self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{__strong __typeof (wself) sself = wself;if (sself) {[sself cancel];[app endBackgroundTask:sself.backgroundTaskId];sself.backgroundTaskId = UIBackgroundTaskInvalid;}}];}
#endifif (self.options & SDWebImageDownloaderIgnoreCachedResponse) {// Grab the cached data for later check  从系统的URL缓存中找下NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:self.request];if (cachedResponse) {self.cachedData = cachedResponse.data;}}NSURLSession *session = self.unownedSession;if (!self.unownedSession) {NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];sessionConfig.timeoutIntervalForRequest = 15;//创建一个session对象,因为后面要创建NSURLSessionTask,需要session对象self.ownedSession = [NSURLSession sessionWithConfiguration:sessionConfigdelegate:selfdelegateQueue:nil];session = self.ownedSession;}self.dataTask = [session dataTaskWithRequest:self.request];self.executing = YES;}// 开启下载任务[self.dataTask resume];if (self.dataTask) {// 通知进度,当前下载大小0,未知总长度,下载的URLfor (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {progressBlock(0, NSURLResponseUnknownLength, self.request.URL);}// 发出通知,开始下载__weak typeof(self) weakSelf = self;dispatch_async(dispatch_get_main_queue(), ^{[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:weakSelf];});} #if SD_UIKITClass UIApplicationClass = NSClassFromString(@"UIApplication");if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {return;}if (self.backgroundTaskId != UIBackgroundTaskInvalid) {UIApplication * app = [UIApplication performSelector:@selector(sharedApplication)];[app endBackgroundTask:self.backgroundTaskId];self.backgroundTaskId = UIBackgroundTaskInvalid;}
#endif
}

这里就是通过一个session对象和一个request对象创建了一个dataTask对象,这个dataTask对象才是真正用来下载的,然后调用[self.dataTask resume]执行下载。

当然下载的过程中会通过回调返回当前的下载进度

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {[self.imageData appendData:data];// 对图片的二进制流进行处理,应该对对图片编解码的,看不太懂if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0) {......}// 下载进度的回调for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {progressBlock(self.imageData.length, self.expectedSize, self.request.URL);}
}

用来存储回调的数据结构是一个NSMutableDictionary,其中key是图片的url,value是回调的数组,数组中是一个个的block
举个例子,存储后应该是这样的,

@{@"http://imageurl":[@{@"progress":progressBlock1,@"completed":completedBlock1,},@{@"progress":progressBlock2,@"completed":completedBlock2,},],//其他
}

对于二进制文件 都会有头信息,SDWebImage就根据头部信息来获取图片类型


+ (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data {if (!data) {return SDImageFormatUndefined;}uint8_t c;[data getBytes:&c length:1];switch (c) {case 0xFF:return SDImageFormatJPEG;case 0x89:return SDImageFormatPNG;case 0x47:return SDImageFormatGIF;case 0x49:case 0x4D:return SDImageFormatTIFF;case 0x52:// R as RIFF for WEBPif (data.length < 12) {return SDImageFormatUndefined;}NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {return SDImageFormatWebP;}}return SDImageFormatUndefined;
}

到这里SDWebImage的源码分析就结束啦。SDWebImage每个类都很小(超过500行的都不多,配合流程图还是比较好理解的),最后放下github上官方给出的流程和各个类之间的联系.



http://chatgpt.dhexx.cn/article/kHN92wmu.shtml

相关文章

SDWebImage的缓存策略

SDWebImage 相信对大多数开发者来说&#xff0c;都是一个不陌生的名字。它除了帮助我们读取网络图片&#xff0c;还会处理这些图片的缓存。它的缓存机制到底是什么样的呢&#xff0c;让我给跟大家唠叨唠叨&#xff0c;希望你能有收获。 基本结构 闲言少叙&#xff0c;咱们这就…

SDWebImage源码解析

这段时间研究了一下SDWebImage源码&#xff0c;因个人能力有限&#xff0c;如有不对的地方&#xff0c;请指出或留言&#xff0c;谢谢&#xff01; SDWebImage是一个开源的第三方库&#xff0c;它提供了UIImageView的一个分类&#xff0c;以支持从远程服务器下载并缓存图片的功…

【iOS】SDWebImage

文章目录 SDWebImage日常使用一些主要功能获取图片缓存 缓存机制独立的异步图像下载独立的异步图像缓存 图片加载全过程 源码分析架构图结构SDWebImageManager1. SDWebImageOptions2. SDWebImageManagerDelegate SDWebImageCompatSDWebImageCompat.hSDWebImageCompat.m sd_imag…

开源框架:SDWebImage

引言: SDWebImage是我搞iOS以来少数佩服的框架,膜拜一下作者.真的写的非常棒! 这套开源框架还是蛮重要的, 涉及到异步加载图片源和自动缓存. 我们如果能够熟练使用其API 就可以实现很多复杂的需求了. 作者依旧在更新,目前3.0 版本已经非常强大! 简化了更多的API接口.加强了…

iOS 第三方框架-SDWebImage解读

在iOS的图片加载框架中&#xff0c;SDWebImage可谓是占据大半壁江山。它支持从网络中下载且缓存图片&#xff0c;并设置图片到对应的UIImageView控件或者UIButton控件。在项目中使用SDWebImage来管理图片加载相关操作可以极大地提高开发效率&#xff0c;让我们更加专注于业务逻…

iOS——SDWebImage解读

前言 在iOS的图片加载框架中&#xff0c;SDWebImage占据了大半壁江山。它提供了UIImageView的一个分类&#xff0c;支持从网络中下载且缓存图片&#xff0c;并设置图片到对应的UIImageView控件或者UIButton控件。在项目中使用SDWebImage来管理图片加载相关操作可以极大地提高开…

SDWebImage异步加载图片及缓存的管理与清理

一、SDWebImage介绍 1、在项目的开发过程中&#xff0c;我们经常会用到异步加载图片的功能&#xff0c;先从网络上异步下载图片&#xff0c;然后通过UIImageView显示在屏幕上。这是一个经常使用的功能&#xff0c;基本上所有的联网应用程序都要用到的功能&#xff0c;现在GitH…

iOS SDWebImage详细介绍

在iOS的图片加载框架中&#xff0c;SDWebImage使用频率非常高。它支持从网络中下载且缓存图片&#xff0c;并设置图片到对应的UIImageView控件或者UIButton控件。在项目中使用SDWebImage来管理图片加载相关操作可以极大地提高开发效率&#xff0c;让我们更加专注于业务逻辑实现…

JNA入门

1&#xff0c;什么是JNA&#xff1f; JNA全称Java Native Access&#xff0c;是一个建立在JNI技术之上的Java开源框架。 2&#xff0c;JNA有什么用&#xff1f; Java开发过程中&#xff0c;有时候会需要和C&#xff0c;C等交互&#xff0c;JNA相当于中间的适配器 3&#xf…

JNA —— Java调用C/C++动态库

工作所需&#xff0c;要使用Java调用c/c的动态库&#xff0c;实现Java程序使用动态库中的函数。 搜索了一番&#xff0c;常用的有JNI、JNA方法。 JNI&#xff08;Java Native Interface&#xff09; JNI定义了一种公用的语法&#xff0c;当Java和c/c都遵循这样的语法时就可以互…

关于JAVA中的JNA

1、jna是什么 jna是java native access的简称&#xff0c;用他可以调用C、C代码&#xff0c;特别是windows中强大的库文件&#xff08;dll&#xff0c;在linux下是so文件&#xff09;&#xff0c;这样java就可以操控底层的一些东西&#xff0c;比如调用加密机、智能卡之类的 2、…

JNA与JNI谁更受青睐呢

JNA(Java Native Access)框架是一个开源的Java框架&#xff0c;是SUN公司主导开发的&#xff0c;建立在经典的JNI的基础之上的一个框架。非常强大、易用。其中JNA是对JNI的封装&#xff0c;能让java使用者更好的使用本地的动态库 一、JNA与JNI的比较 JNI: JNI允许Java代码和其…

JNA实战系列:第一个简单的JNA开发程序

文章目录 第一个简单的JNA的开发程序一、引入依赖包二、创建一个接口&#xff0c;继承Libary类三、在Java中使用dll中封装的方法总结:JNA中调用C、CDLL或者.so库中的函数的步骤总结:思考 第一个简单的JNA的开发程序 假如有一个helloworld.h和一个helloworld.dll文件&#xff0…

Java通过JNA调用so库 Linux环境

记录一下自己的JNA调研成果&#xff0c;需求是公司同事用C写了一个红外测温SDK&#xff0c;编译成so文件后提供给客户使用。客户需要一个Linux环境用Java调用so库的一个demo&#xff0c;刚好就我一个懂点Java&#xff0c;所有有了这次调研。 因为JNA相关资料实在太少&#xff…

JNI便捷开发框架JNA框架之入门(一)

一、JNA介绍 JNA(Java Native Access)框架是一个开源的Java框架&#xff0c;是SUN公司主导开发的&#xff0c;建立在经典的JNI的基础之上的一个框架。它提供一组Java工具类用于在运行期动态访问系统本地共享类库而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中…

JNA简介

2019独角兽企业重金招聘Python工程师标准>>> JNA JNA&#xff08;Java Native Access &#xff09;提供一组Java工具类用于在运行期动态访问系统本地库&#xff08;native library&#xff1a;如Window的dll&#xff09;而不需要编写任何Native/JNI代码。开发人员只…

JNA的概念

1. JNA简单介绍 先说JNI(Java Native Interface)吧&#xff0c;有过不同语言间通信经历的一般都知道&#xff0c;它允许Java代码和其他语言&#xff08;尤其C/C&#xff09;写的代码进行交互&#xff0c;只要遵守调用约定即可。首先看下JNI调用C/C的过程&#xff0c;注意写程序…

Java 之 JNA(调用第三方库)

是什么&#xff1f; 一、了解JNA之前&#xff0c;我们先了解一下JNA的前身JNI&#xff08;Java Native Interface&#xff09;&#xff1a;通过使用 Java本地接口书写程序&#xff0c;可以确保代码在不同的平台上方便移植。 [1] 从Java1.1开始&#xff0c;JNI标准成为java平台…

JNA实战笔记汇总(一)—— JNA简介及demo环境创建

目录 1、简介 2、原理 3、配置环境&#xff0c;创建demo 3.1 搞清楚.dll/.so文件适用环境 3.2 创建一个普通的maven项目 3.2.1 将.dll/.so文件放在resources根路径下 3.2.2 pom.xml文件添加jna依赖 3.2.3 编写一个CLibrary接口&#xff0c;继承Library接口 3.2.4 编写…

ROW(行)与COLUMN(列)

数列数&#xff0c;COLUMNS&#xff08;A:B&#xff09;2&#xff0c;即A到B一共两列。