相信對於廣大的iOS開發者,對SDWebImage並不會陌生,這個框架經過給UIImageView和UIButton添加分類,實現一個異步下載圖片而且支持緩存的功能。整個框架的接口很是簡潔,每一個類的分工都很明確,是很值得你們學習的。git
在使用這個框架的時候,只須要提供一個下載的url和佔位圖就能夠在回調裏拿到下載後的圖片:程序員
[imageview sd_setImageWithURL:[NSURL URLWithString:@"pic.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder"] completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
imageview.image = image;
NSLog(@"圖片加載完成");
}];
複製代碼
並且咱們還能夠不設置佔位圖片,也能夠不使用回調的block,很是靈活:github
//圖片下載完成後直接顯示下載後的圖片
[imageview sd_setImageWithURL:[NSURL URLWithString:@"pic.jpg"]];
複製代碼
在最開始先簡單介紹這個框架:編程
這個框架的核心類是SDWebImageManger
,在外部有UIImageView+WebCache
和 UIButton+WebCache
爲下載圖片的操做提供接口。內部有SDWebImageManger
負責處理和協調 SDWebImageDownloader
和 SDWebImageCache
:SDWebImageDownloader
負責具體的下載任務,SDWebImageCache
負責關於緩存的工做:添加,刪除,查詢緩存。數組
首先咱們大體看一下這個框架的調用流程圖:緩存
從這個流程圖裏能夠大體看出,該框架分爲兩個層:UIKit層(負責接收下載參數)和工具層(負責下載操做和緩存)。安全
OK~基本流程大概清楚了,咱們看一下每一個層具體實現吧~網絡
該框架最外層的類是UIImageView +WebCache
,咱們將圖片的URL,佔位圖片直接給這個類。下面是這個類的公共接口:session
// ============== UIImageView + WebCache.h ============== //
- (void)sd_setImageWithURL:(nullable NSURL *)url;
- (void)sd_setImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder;
- (void)sd_setImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options;
- (void)sd_setImageWithURL:(nullable NSURL *)url
completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
completed:(nullable SDExternalCompletionBlock)completedBlock;
- (void)sd_setImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock;
複製代碼
能夠看出,這個類提供的接口很是靈活,能夠根據咱們本身的需求來調用其中某一個方法,而這些方法到最後都會走到:多線程
// ============== UIView+ WebCache.m ============== //
- (void)sd_setImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock;
複製代碼
而這個方法裏面,調用的是UIView+WebCache
分類的:
// ============== UIView+ WebCache.m ============== //
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
operationKey:(nullable NSString *)operationKey
setImageBlock:(nullable SDSetImageBlock)setImageBlock
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock;
複製代碼
爲何不是UIImageView+WebCache而要上一層到UIView的分類裏呢? 由於SDWebImage框架也支持UIButton的下載圖片等方法,因此須要在它們的父類:UIView裏面統一一個下載方法。
簡單看一下這個方法的實現(省略的代碼用...代替):
// ============== UIView+ WebCache.m ============== //
//valid key:UIImageView || UIButton
NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
//UIView+WebCacheOperation 的 operationDictionary
//下面這行代碼是保證沒有當前正在進行的異步下載操做, 使它不會與即將進行的操做發生衝突
[self sd_cancelImageLoadOperationWithKey:validOperationKey];
//添加臨時的佔位圖(在不延遲添加佔位圖的option下)
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
});
}
//若是url存在
if (url) {
...
__weak __typeof(self)wself = self;
//SDWebImageManager下載圖片
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
...
//dispatch_main_sync_safe : 保證block能在主線程進行
dispatch_main_async_safe(^{
if (!sself) {
return;
}
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
//image,並且不自動替換 placeholder image
completedBlock(image, error, cacheType, url);
return;
} else if (image) {
//存在image,須要立刻替換 placeholder image
[sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock];
[sself sd_setNeedsLayout];
} else {
//沒有image,在圖片下載完以後顯示 placeholder image
if ((options & SDWebImageDelayPlaceholder)) {
[sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
[sself sd_setNeedsLayout];
}
}
if (completedBlock && finished) {
completedBlock(image, error, cacheType, url);
}
});
}];
//在操做緩存字典(operationDictionary)裏添加operation,表示當前的操做正在進行
[self sd_setImageLoadOperation:operation forKey:validOperationKey];
} else {
//若是url不存在,就在completedBlock裏傳入error(url爲空)
dispatch_main_async_safe(^{
[self sd_removeActivityIndicator];
if (completedBlock) {
NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
completedBlock(nil, error, SDImageCacheTypeNone, url);
}
});
}
```
> 值得一提的是,在這一層,使用一個字典``operationDictionary``專門用做存儲操做的緩存,隨時添加,刪除操做任務。
而這個字典是``UIView+WebCacheOperation``分類的關聯對象,它的存取方法使用運行時來操做:
```objc
// ============== UIView+WebCacheOperation.m ============== //
//獲取關聯對象:operations(用來存放操做的字典)
- (SDOperationsDictionary *)operationDictionary {
SDOperationsDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey);
//存放操做的字典
if (operations) {
return operations;
}
//若是沒有,就新建一個
operations = [NSMutableDictionary dictionary];
objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return operations;
}
複製代碼
爲何不直接在
UIImageView+WebCache
裏直接關聯這個對象呢?我以爲這裏做者應該是聽從面向對象的單一職責原則(SRP:Single responsibility principle),就連類都要履行這個職責,況且分類呢?這裏做者專門創造一個分類UIView+WebCacheOperation
來管理操做緩存(字典)。
到這裏,UIKit
層上面的東西都講完了,如今開始正式講解工具層。
上文提到過,SDWebImageManager
同時管理SDImageCache
和SDWebImageDownloader
兩個類,它是這一層的老大哥。在下載任務開始的時候,SDWebImageManager
首先訪問SDImageCache
來查詢是否存在緩存,若是有緩存,直接返回緩存的圖片。若是沒有緩存,就命令SDWebImageDownloader
來下載圖片,下載成功後,存入緩存,顯示圖片。以上是SDWebImageManager
大體的工做流程。
在詳細講解SDWebImageManager
是如何下載圖片以前,咱們先看一下這個類的幾個重要的屬性:
// ============== SDWebImageManager.h ============== //
@property (strong, nonatomic, readwrite, nonnull) SDImageCache *imageCache;//管理緩存
@property (strong, nonatomic, readwrite, nonnull) SDWebImageDownloader //下載器*imageDownloader;
@property (strong, nonatomic, nonnull) NSMutableSet<NSURL *> *failedURLs;//記錄失效url的名單
@property (strong, nonatomic, nonnull) NSMutableArray<SDWebImageCombinedOperation *> *runningOperations;//記錄當前正在執行的操做
複製代碼
SDWebImageManager
下載圖片的方法只有一個:
[SDWebImageManager.sharedManager loadImageWithURL:options:progress:completed:]
複製代碼
看一下這個方法的具體實現:
// ============== SDWebImageManager.m ============== //
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock {
...
//在SDImageCache裏查詢是否存在緩存的圖片
operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
...
//(沒有緩存圖片) || (即便有緩存圖片,也須要更新緩存圖片) || (代理沒有響應imageManager:shouldDownloadImageForURL:消息,默認返回yes,須要下載圖片)|| (imageManager:shouldDownloadImageForURL:返回yes,須要下載圖片)
if ((!cachedImage || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
//1. 存在緩存圖片 && 即便有緩存圖片也要下載更新圖片
if (cachedImage && options & SDWebImageRefreshCached) {
[self callCompletionBlockForOperation:weakOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
}
// 2. 若是不存在緩存圖片
...
//開啓下載器下載
//subOperationToken 用來標記當前的下載任務,便於被取消
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) {
// 1. 若是任務被取消,則什麼都不作,避免和其餘的completedBlock重複
} else if (error) {
//2. 若是有錯誤
//2.1 在completedBlock裏傳入error
[self callCompletionBlockForOperation:strongOperation completion:completedBlock error:error url:url];
//2.2 在錯誤url名單中添加當前的url
if ( error.code != NSURLErrorNotConnectedToInternet
&& error.code != NSURLErrorCancelled
&& error.code != NSURLErrorTimedOut
&& error.code != NSURLErrorInternationalRoamingOff
&& error.code != NSURLErrorDataNotAllowed
&& error.code != NSURLErrorCannotFindHost
&& error.code != NSURLErrorCannotConnectToHost) {
@synchronized (self.failedURLs) {
[self.failedURLs addObject:url];
}
}
}
else {
//3. 下載成功
//3.1 若是須要下載失敗後從新下載,則將當前url從失敗url名單裏移除
if ((options & SDWebImageRetryFailed)) {
@synchronized (self.failedURLs) {
[self.failedURLs removeObject:url];
}
}
//3.2 進行緩存
BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) {
//(即便緩存存在,也要刷新圖片) && 緩存圖片 && 不存在下載後的圖片:不作操做
} else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
//(下載圖片成功 && (沒有動圖||處理動圖) && (下載以後,緩存以前處理圖片) 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];
}
//將圖片傳入completedBlock
[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];
}
}
//若是完成,從當前運行的操做列表裏移除當前操做
if (finished) {
[self safelyRemoveOperationFromRunning:strongOperation];
}
}];
//取消的block
operation.cancelBlock = ^{
//取消當前的token
[self.imageDownloader cancel:subOperationToken];
__strong __typeof(weakOperation) strongOperation = weakOperation;
//從當前運行的操做列表裏移除當前操做
[self safelyRemoveOperationFromRunning:strongOperation];
};
} else if (cachedImage) {
//存在緩存圖片
__strong __typeof(weakOperation) strongOperation = weakOperation;
//調用完成的block
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
//刪去當前的的下載操做(線程安全)
[self safelyRemoveOperationFromRunning:operation];
} else {
//沒有緩存的圖片,並且下載被代理終止了
__strong __typeof(weakOperation) strongOperation = weakOperation;
// 調用完成的block
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
//刪去當前的下載操做
[self safelyRemoveOperationFromRunning:operation];
}
}];
return operation;
}
複製代碼
看完了SDWebImageManager
的回調處理,咱們分別看一下 SDImageCache
和SDWebImageDownloader
內部具體是如何工做的。首先看一下SDImageCache
:
// ============== SDImageCache.m ============== //
@property (strong, nonatomic, nonnull) NSCache *memCache;//內存緩存
@property (strong, nonatomic, nonnull) NSString *diskCachePath;//磁盤緩存路徑
@property (strong, nonatomic, nullable) NSMutableArray<NSString *> *customPaths;//
@property (SDDispatchQueueSetterSementics, nonatomic, nullable) dispatch_queue_t //ioQueue惟一子線程;
複製代碼
// ============== SDImageCache.m ============== //
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock {
if (!key) {
if (doneBlock) {
doneBlock(nil, nil, SDImageCacheTypeNone);
}
return nil;
}
//================查看內存的緩存=================//
UIImage *image = [self imageFromMemoryCacheForKey:key];
// 若是存在,直接調用block,將image,data,CaheType傳進去
if (image) {
NSData *diskData = nil;
//若是是gif,就拿到data,後面要傳到doneBlock裏。不是gif就傳nil
if ([image isGIF]) {
diskData = [self diskImageDataBySearchingAllPathsForKey:key];
}
if (doneBlock) {
doneBlock(image, diskData, SDImageCacheTypeMemory);
}
//由於圖片有緩存可供使用,因此不用實例化NSOperation,直接範圍nil
return nil;
}
//================查看磁盤的緩存=================//
NSOperation *operation = [NSOperation new];
//惟一的子線程:self.ioQueue
dispatch_async(self.ioQueue, ^{
if (operation.isCancelled) {
// 在用以前就判斷operation是否被取消了,做者考慮的很是嚴謹
return;
}
@autoreleasepool {
NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage && self.config.shouldCacheImagesInMemory) {
// cost 被用來計算緩存中全部對象的代價。當內存受限或者全部緩存對象的總代價超過了最大容許的值時,緩存會移除其中的一些對象。
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;
}
複製代碼
// ============== SDWebImageDownloader.m ============== //
@property (strong, nonatomic, nonnull) NSOperationQueue *downloadQueue;//下載隊列
@property (weak, nonatomic, nullable) NSOperation *lastAddedOperation;//最後添加的下載操做
@property (assign, nonatomic, nullable) Class operationClass;//操做類
@property (strong, nonatomic, nonnull) NSMutableDictionary<NSURL *, SDWebImageDownloaderOperation *> *URLOperations;//操做數組
@property (strong, nonatomic, nullable) SDHTTPHeadersMutableDictionary *HTTPHeaders;//HTTP請求頭
@property (SDDispatchQueueSetterSementics, nonatomic, nullable) dispatch_queue_t barrierQueue;//用來阻塞前面的下載線程(串行化)
複製代碼
// ============== SDWebImageDownloader.m ============== //
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
__weak SDWebImageDownloader *wself = self;
return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{
__strong __typeof (wself) sself = wself;
NSTimeInterval timeoutInterval = 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
//建立下載請求
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval: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用於請求網絡資源的操做,它是一個 NSOperation 的子類
SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
operation.shouldDecompressImages = sself.shouldDecompressImages;
//url證書
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) {
// Emulate LIFO execution order by systematically adding new operations as last operation's dependency
//addDependency:參數opertaion倍添加到NSOperationQueue後,只有等該opertion結束後才能執行其餘的operation,實現了後進先出
[sself.lastAddedOperation addDependency:operation];
sself.lastAddedOperation = operation;
}
return operation;
}];
}
複製代碼
這裏面還有一個addProgressCallback: progressBlock: completedBlock: forURL: createCallback:
方法,用來保存progressBlock
和completedBlock
。咱們看一下這個方法的實現:
// ============== SDWebImageDownloader.m ============== //
- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
forURL:(nullable NSURL *)url
createCallback:(SDWebImageDownloaderOperation *(^)())createCallback {
// url 用來做爲回調字典的key,若是爲空,當即返回失敗
if (url == nil) {
if (completedBlock != nil) {
completedBlock(nil, nil, nil, NO);
}
return nil;
}
__block SDWebImageDownloadToken *token = nil;
//串行化前面全部的操做
dispatch_barrier_sync(self.barrierQueue, ^{
//當前下載操做中取出SDWebImageDownloaderOperation實例
SDWebImageDownloaderOperation *operation = self.URLOperations[url];
if (!operation) {
//若是沒有,就初始化它
operation = createCallback();
self.URLOperations[url] = operation;
__weak SDWebImageDownloaderOperation *woperation = operation;
operation.completionBlock = ^{
SDWebImageDownloaderOperation *soperation = woperation;
if (!soperation) return;
if (self.URLOperations[url] == soperation) {
[self.URLOperations removeObjectForKey:url];
};
};
}
id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
//這裏 downloadOperationCancelToken 默認是一個字典,存放 progressBlock 和 completedBlock
token = [SDWebImageDownloadToken new];
token.url = url;
token.downloadOperationCancelToken = downloadOperationCancelToken;
});
return token;
}
複製代碼
這裏真正保存兩個block的方法是addHandlersForProgress: completed:
:
- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
//實例化一個SDCallbacksDictionary,存放一個progressBlock 和 completedBlock
SDCallbacksDictionary *callbacks = [NSMutableDictionary new];
if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
dispatch_barrier_async(self.barrierQueue, ^{
//添加到緩存中 self.callbackBlocks
[self.callbackBlocks addObject:callbacks];
});
return callbacks;
}
複製代碼
到這裏SDWebImage
的核心方法都講解完畢了,其餘沒有講到的部分之後會慢慢添加上去。
存:
objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//將operations對象關聯給self,地址爲&loadOperationKey,語義是OBJC_ASSOCIATION_RETAIN_NONATOMIC。
複製代碼
取:
SDOperationsDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey);
//將operations對象經過地址&loadOperationKey從self裏取出來
複製代碼
//給self.runningOperations加鎖
//self.runningOperations數組的添加操做
@synchronized (self.runningOperations) {
[self.runningOperations addObject:operation];
}
//self.runningOperations數組的刪除操做
- (void)safelyRemoveOperationFromRunning:(nullable SDWebImageCombinedOperation*)operation {
@synchronized (self.runningOperations) {
if (operation) {
[self.runningOperations removeObject:operation];
}
}
}
複製代碼
dispatch_main_async_safe(^{
//將下面這段代碼放在主線程中
[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
});
//宏定義:
#define dispatch_main_async_safe(block)\
if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
#endif
複製代碼
- (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader {
if ((self = [super init])) {
_imageCache = cache;
_imageDownloader = downloader;
_failedURLs = [NSMutableSet new];
_runningOperations = [NSMutableArray new];
}
return self;
}
複製代碼
若是在參數裏添加了nonnull關鍵字,那麼編譯器就能夠檢查傳入的參數是否爲nil,若是是,則編譯器會有警告
if ([url isKindOfClass:NSString.class]) {
url = [NSURL URLWithString:(NSString *)url];
}
複製代碼
在傳入的參數爲NSString時(可是方法參數要求是NSURL),自動轉換爲NSURL
貌似還有圖片解碼等內容沒有詳細看,之後會逐漸補充噠~
本文已經同步到個人我的技術博客:傳送門
---------------------------- 2018年7月17日更新 ----------------------------
注意注意!!!
筆者在近期開通了我的公衆號,主要分享編程,讀書筆記,思考類的文章。
由於公衆號天天發佈的消息數有限制,因此到目前爲止尚未將全部過去的精選文章都發布在公衆號上,後續會逐步發佈的。
並且由於各大博客平臺的各類限制,後面還會在公衆號上發佈一些短小精幹,以小見大的乾貨文章哦~
掃下方的公衆號二維碼並點擊關注,期待與您的共同成長~