iOS AFN AFImageDownloader 源碼分析

AFN AFImageDownloader 源碼分析

源碼也是很簡(jiǎn)單,再次來(lái)學(xué)人家的思想,其實(shí)讀源碼最終就是學(xué)習(xí)人家的設(shè)計(jì)和思想,到最后你會(huì)變成一個(gè)特別喜歡讀源碼的人

AFImageDownloaderResponseHandler

我們正常在設(shè)計(jì)接口的時(shí)候,都會(huì)留給別人一個(gè)成功或者失敗的回調(diào),但有沒(méi)有想過(guò)一個(gè)問(wèn)題,比如我一個(gè)操作調(diào)用多次,但這個(gè)操作是重復(fù)的,但我只關(guān)心成功或者失敗的回調(diào),那么需要怎么辦?或者換句話說(shuō),我們平時(shí)調(diào)用代理的時(shí)候,設(shè)置代理,都是設(shè)置單一代理,那么這個(gè)代理,最終只會(huì)被回到一次到一個(gè)地方,我們也是會(huì)有需求,設(shè)置多個(gè)代理,那么怎么做呢?

這里作者將一個(gè)接口中的成功或者失敗額回調(diào),抽象到一個(gè)具體的類里面去保存,而之后將這個(gè)類保存到一個(gè)字典中,每次的key不同,就能實(shí)現(xiàn),多次回調(diào)block或者代理的需求了

AFImageDownloaderMergedTask

這個(gè)類就是對(duì)下載圖片task的一個(gè)封裝

@interface AFImageDownloaderMergedTask : NSObject
@property (nonatomic, strong) NSString *URLIdentifier;
@property (nonatomic, strong) NSUUID *identifier;
@property (nonatomic, strong) NSURLSessionDataTask *task;
@property (nonatomic, strong) NSMutableArray <AFImageDownloaderResponseHandler*> *responseHandlers;

@end

可以看到他保存著請(qǐng)求的url,id標(biāo)識(shí),任務(wù)task,和上面所說(shuō)的回調(diào)數(shù)組,也就是說(shuō)一個(gè)task可以有多個(gè)response回調(diào)block

AFImageDownloader



+ (NSURLCache *)defaultURLCache {
    // 這篇文章不錯(cuò):https://www.cnblogs.com/madpanda/p/4700741.html
    NSUInteger memoryCapacity = 20 * 1024 * 1024; // 20MB
    NSUInteger diskCapacity = 150 * 1024 * 1024; // 150MB
    NSURL *cacheURL = [[[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory
                                                              inDomain:NSUserDomainMask
                                                     appropriateForURL:nil
                                                                create:YES
                                                                 error:nil]
                       URLByAppendingPathComponent:@"com.alamofire.imagedownloader"];
    
#if TARGET_OS_MACCATALYST
    return [[NSURLCache alloc] initWithMemoryCapacity:memoryCapacity
                                         diskCapacity:diskCapacity
                                         directoryURL:cacheURL];
#else
    return [[NSURLCache alloc] initWithMemoryCapacity:memoryCapacity
                                         diskCapacity:diskCapacity
                                             diskPath:[cacheURL path]];
#endif
}

+ (NSURLSessionConfiguration *)defaultURLSessionConfiguration {
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];

    //TODO set the default HTTP headers

    configuration.HTTPShouldSetCookies = YES;
    configuration.HTTPShouldUsePipelining = NO;

    configuration.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;
    configuration.allowsCellularAccess = YES;
    configuration.timeoutIntervalForRequest = 60.0;
    configuration.URLCache = [AFImageDownloader defaultURLCache];

    return configuration;
}

這里會(huì)造一個(gè)NSURLCache的類,用于緩存,主要是配置個(gè)系統(tǒng)的NSURLSessionConfiguration這個(gè)類,是依據(jù)這個(gè)屬性的requestCachePolicy,也就是說(shuō)緩存策略,是否從緩存中讀取,如果這個(gè)策略中選擇了從緩存中讀取,那么我們下載完圖片之后會(huì)存放在緩存中,下次同一個(gè)請(qǐng)求,就可以不請(qǐng)求了,直接從緩存中讀取圖片就可以了,后面會(huì)講到,,這里可以看到,申請(qǐng)內(nèi)存緩存20M,磁盤(pán)緩存250M,

- (instancetype)initWithSessionManager:(AFHTTPSessionManager *)sessionManager
                downloadPrioritization:(AFImageDownloadPrioritization)downloadPrioritization
                maximumActiveDownloads:(NSInteger)maximumActiveDownloads
                            imageCache:(id <AFImageRequestCache>)imageCache {
    if (self = [super init]) {
        self.sessionManager = sessionManager;

        // 下載策略,先進(jìn)先出,還是先進(jìn)后出
        self.downloadPrioritization = downloadPrioritization;
        // 允許最大下載個(gè)數(shù)
        self.maximumActiveDownloads = maximumActiveDownloads;
        // 圖片緩存實(shí)現(xiàn),AFN提供了緩存策略,也可以自己實(shí)現(xiàn)
        self.imageCache = imageCache;

        // 隊(duì)列中的任務(wù)
        self.queuedMergedTasks = [[NSMutableArray alloc] init];
        self.mergedTasks = [[NSMutableDictionary alloc] init];
        self.activeRequestCount = 0;

        NSString *name = [NSString stringWithFormat:@"com.alamofire.imagedownloader.synchronizationqueue-%@", [[NSUUID UUID] UUIDString]];
    // 事件觸發(fā)同步隊(duì)列
        self.synchronizationQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL);

        // 響應(yīng)隊(duì)列
        name = [NSString stringWithFormat:@"com.alamofire.imagedownloader.responsequeue-%@", [[NSUUID UUID] UUIDString]];
        self.responseQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);
    }

    return self;
}

- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
                                                  withReceiptID:(nonnull NSUUID *)receiptID
                                                        success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse  * _Nullable response, UIImage *responseObject))success
                                                        failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure {
    __block NSURLSessionDataTask *task = nil;
    // 進(jìn)入到同步串行隊(duì)列
    dispatch_sync(self.synchronizationQueue, ^{
        // url 為唯一標(biāo)識(shí)符
        NSString *URLIdentifier = request.URL.absoluteString;
        if (URLIdentifier == nil) {
            if (failure) {
                NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorBadURL userInfo:nil];
                dispatch_async(dispatch_get_main_queue(), ^{
                    failure(request, nil, error);
                });
            }
            return;
        }

        // 1) Append the success and failure blocks to a pre-existing request if it already exists
        // url 為存儲(chǔ)任務(wù)的唯一標(biāo)識(shí)
        AFImageDownloaderMergedTask *existingMergedTask = self.mergedTasks[URLIdentifier];
        if (existingMergedTask != nil) {
            // 如果找到了task,說(shuō)明這個(gè)url已經(jīng)請(qǐng)求過(guò)了,已經(jīng)有存在的任務(wù)了
            // 然后根據(jù)傳進(jìn)來(lái)的receiptID創(chuàng)建回調(diào)封裝類
            AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID success:success failure:failure];
            // 將回調(diào)封裝類添加到這個(gè)類里面,方面以后多次回調(diào)
            [existingMergedTask addResponseHandler:handler];
            task = existingMergedTask.task;
            return;
        }

        // 2) Attempt to load the image from the image cache if the cache policy allows it
        // 判斷一下緩存策略
        switch (request.cachePolicy) {
            case NSURLRequestUseProtocolCachePolicy:
            case NSURLRequestReturnCacheDataElseLoad:
            case NSURLRequestReturnCacheDataDontLoad: {
                // 如果策略中包含從緩存中讀取,那么就調(diào)用我們之前的 imageCache 工廠,取出我們已經(jīng)緩存的圖片,就不用每次都請(qǐng)求加載了
                UIImage *cachedImage = [self.imageCache imageforRequest:request withAdditionalIdentifier:nil];
                if (cachedImage != nil) {
                    if (success) {
                        dispatch_async(dispatch_get_main_queue(), ^{
                            success(request, nil, cachedImage);
                        });
                    }
                    return;
                }
                break;
            }
            default:
                break;
        }

        // 3) Create the request and set up authentication, validation and response serialization
        NSUUID *mergedTaskIdentifier = [NSUUID UUID];
        NSURLSessionDataTask *createdTask;
        __weak __typeof__(self) weakSelf = self;

        // 開(kāi)始異步請(qǐng)求
        createdTask = [self.sessionManager
                       dataTaskWithRequest:request
                       uploadProgress:nil
                       downloadProgress:nil
                       completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
            // 有返回結(jié)果了之后,進(jìn)入我們的回調(diào)隊(duì)列
                           dispatch_async(self.responseQueue, ^{
                               __strong __typeof__(weakSelf) strongSelf = weakSelf;
                               // 從我們的緩存中,根據(jù)url取出來(lái)我們保存的已經(jīng)創(chuàng)建好的task封裝類
                               AFImageDownloaderMergedTask *mergedTask = [strongSelf safelyGetMergedTask:URLIdentifier];
                               if ([mergedTask.identifier isEqual:mergedTaskIdentifier]) {
                                   // 然后移除
                                   mergedTask = [strongSelf safelyRemoveMergedTaskWithURLIdentifier:URLIdentifier];
                                   if (error) {
                                       // 如果發(fā)生失敗,那么從我們的task封裝類里面,取出回調(diào)封裝類,所有的,然后調(diào)用這個(gè)封裝類里面的失敗的回調(diào),一次回調(diào)多次
                                       for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) {
                                           if (handler.failureBlock) {
                                               dispatch_async(dispatch_get_main_queue(), ^{
                                                   handler.failureBlock(request, (NSHTTPURLResponse *)response, error);
                                               });
                                           }
                                       }
                                   } else {
                                       // 成功了之后,選擇是否將圖片緩存,默認(rèn)是允許緩存
                                       if ([strongSelf.imageCache shouldCacheImage:responseObject forRequest:request withAdditionalIdentifier:nil]) {
                                           // 緩存策略為,key為url拼接AdditionalIdentifier這個(gè)字符串,因?yàn)檫@里傳的nil,所以key就為url,value就為下載的這個(gè)圖片數(shù)據(jù)
                                           [strongSelf.imageCache addImage:responseObject forRequest:request withAdditionalIdentifier:nil];
                                       }

                                       // 取出所有的回調(diào),依次回調(diào)
                                       for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) {
                                           if (handler.successBlock) {
                                               dispatch_async(dispatch_get_main_queue(), ^{
                                                   handler.successBlock(request, (NSHTTPURLResponse *)response, responseObject);
                                               });
                                           }
                                       }
                                       
                                   }
                               }
                               // 將當(dāng)前請(qǐng)求的數(shù)據(jù)-1
                               [strongSelf safelyDecrementActiveTaskCount];
                               // 拿出下一個(gè)請(qǐng)求,進(jìn)行請(qǐng)求
                               [strongSelf safelyStartNextTaskIfNecessary];
                           });
                       }];

        // 4) Store the response handler for use when the request completes
        // 根據(jù)穿件來(lái)的 receiptID 創(chuàng)建封裝回調(diào)類,將成功和失敗回調(diào)封裝進(jìn)去
        AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID
                                                                                                   success:success
                                                                                                   failure:failure];
        // 創(chuàng)建task封裝類,將url和taskID還有當(dāng)前task傳進(jìn)去保存
        AFImageDownloaderMergedTask *mergedTask = [[AFImageDownloaderMergedTask alloc]
                                                   initWithURLIdentifier:URLIdentifier
                                                   identifier:mergedTaskIdentifier
                                                   task:createdTask];
        // 當(dāng)前task保存當(dāng)前封裝的會(huì)叫類
        [mergedTask addResponseHandler:handler];
        // mergedTasks 緩存中保存當(dāng)前task,注意是以 url 為唯一標(biāo)識(shí)
        self.mergedTasks[URLIdentifier] = mergedTask;

        // 5) Either start the request or enqueue it depending on the current active request count
        // 如果當(dāng)前沒(méi)有達(dá)到最大請(qǐng)求上線,那么就進(jìn)行請(qǐng)求,如果達(dá)到了就入隊(duì)
        if ([self isActiveRequestCountBelowMaximumLimit]) {
            [self startMergedTask:mergedTask];
        } else {
            [self enqueueMergedTask:mergedTask];
        }

        task = mergedTask.task;
    });
    if (task) {
        return [[AFImageDownloadReceipt alloc] initWithReceiptID:receiptID task:task];
    } else {
        return nil;
    }
}

這個(gè)方法比較核心,如果發(fā)起來(lái)同一個(gè)請(qǐng)求,會(huì)直接返回已經(jīng)發(fā)起額這個(gè)請(qǐng)求,然后將回調(diào)保存到請(qǐng)求里面,所以就會(huì)出現(xiàn)回調(diào)多次的情況,然后如果我們的緩存策略允許從緩存讀取,也會(huì)優(yōu)先從我們的緩存中讀取,最后返回一個(gè) AFImageDownloadReceipt 對(duì)象,這個(gè)對(duì)象是用來(lái)取消任務(wù)的,


- (void)cancelTaskForImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt {
    dispatch_sync(self.synchronizationQueue, ^{
        // 取出需要取消的tash 的url
        NSString *URLIdentifier = imageDownloadReceipt.task.originalRequest.URL.absoluteString;
        // 從緩存中找出這個(gè)task
        AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier];
        // 從這個(gè)task封裝類的responseHandlers里面,這個(gè)responseHandlers里面存放著所有的回調(diào)類,找到和需要需要的這個(gè)receiptID一樣的那個(gè)回調(diào)
        NSUInteger index = [mergedTask.responseHandlers indexOfObjectPassingTest:^BOOL(AFImageDownloaderResponseHandler * _Nonnull handler, __unused NSUInteger idx, __unused BOOL * _Nonnull stop) {
            return handler.uuid == imageDownloadReceipt.receiptID;
        }];

        if (index != NSNotFound) {
            // 找到這個(gè)回調(diào)之后
            AFImageDownloaderResponseHandler *handler = mergedTask.responseHandlers[index];
            // 移除這個(gè)回調(diào)
            [mergedTask removeResponseHandler:handler];
            NSString *failureReason = [NSString stringWithFormat:@"ImageDownloader cancelled URL request: %@",imageDownloadReceipt.task.originalRequest.URL.absoluteString];
            NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey:failureReason};
            NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo];
            // 然后拋出錯(cuò)誤
            if (handler.failureBlock) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    handler.failureBlock(imageDownloadReceipt.task.originalRequest, nil, error);
                });
            }
        }
        // 因?yàn)楫?dāng)前取消的是單個(gè)任務(wù),因?yàn)橛锌赡軙?huì)一個(gè)任務(wù)請(qǐng)求多次但是ID不一樣,如果里面還有別的回調(diào),那么就繼續(xù)下去,如果他的回調(diào)都刪除光了,其實(shí)就是說(shuō)明,當(dāng)前任務(wù)需要取消了,那么就將任務(wù)取消,因?yàn)闆](méi)有接受回調(diào)了
        if (mergedTask.responseHandlers.count == 0) {
            [mergedTask.task cancel];
             // 任務(wù)取消之后,也從緩存中消失
            [self removeMergedTaskWithURLIdentifier:URLIdentifier];
        }
    });
}

下面就是一些同步串行的增刪取操作

- (AFImageDownloaderMergedTask *)safelyRemoveMergedTaskWithURLIdentifier:(NSString *)URLIdentifier {
    __block AFImageDownloaderMergedTask *mergedTask = nil;
    dispatch_sync(self.synchronizationQueue, ^{
        mergedTask = [self removeMergedTaskWithURLIdentifier:URLIdentifier];
    });
    return mergedTask;
}

//This method should only be called from safely within the synchronizationQueue
- (AFImageDownloaderMergedTask *)removeMergedTaskWithURLIdentifier:(NSString *)URLIdentifier {
    AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier];
    [self.mergedTasks removeObjectForKey:URLIdentifier];
    return mergedTask;
}

- (void)safelyDecrementActiveTaskCount {
    dispatch_sync(self.synchronizationQueue, ^{
        if (self.activeRequestCount > 0) {
            self.activeRequestCount -= 1;
        }
    });
}

- (void)safelyStartNextTaskIfNecessary {
    dispatch_sync(self.synchronizationQueue, ^{
        if ([self isActiveRequestCountBelowMaximumLimit]) {
            while (self.queuedMergedTasks.count > 0) {
                AFImageDownloaderMergedTask *mergedTask = [self dequeueMergedTask];
                if (mergedTask.task.state == NSURLSessionTaskStateSuspended) {
                    [self startMergedTask:mergedTask];
                    break;
                }
            }
        }
    });
}

- (void)startMergedTask:(AFImageDownloaderMergedTask *)mergedTask {
    [mergedTask.task resume];
    ++self.activeRequestCount;
}

- (void)enqueueMergedTask:(AFImageDownloaderMergedTask *)mergedTask {
    switch (self.downloadPrioritization) {
        case AFImageDownloadPrioritizationFIFO:
            [self.queuedMergedTasks addObject:mergedTask];
            break;
        case AFImageDownloadPrioritizationLIFO:
            [self.queuedMergedTasks insertObject:mergedTask atIndex:0];
            break;
    }
}

- (AFImageDownloaderMergedTask *)dequeueMergedTask {
    AFImageDownloaderMergedTask *mergedTask = nil;
    mergedTask = [self.queuedMergedTasks firstObject];
    [self.queuedMergedTasks removeObject:mergedTask];
    return mergedTask;
}

- (BOOL)isActiveRequestCountBelowMaximumLimit {
    return self.activeRequestCount < self.maximumActiveDownloads;
}

- (AFImageDownloaderMergedTask *)safelyGetMergedTask:(NSString *)URLIdentifier {
    __block AFImageDownloaderMergedTask *mergedTask;
    // 串行同步讀取
    dispatch_sync(self.synchronizationQueue, ^(){
        mergedTask = self.mergedTasks[URLIdentifier];
    });
    return mergedTask;
}


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容